2025-07-16 21:06:31 +08:00
|
|
|
|
package usecase
|
|
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
|
"context"
|
2025-07-17 20:10:12 +08:00
|
|
|
|
"strings"
|
2025-07-16 21:06:31 +08:00
|
|
|
|
"time"
|
|
|
|
|
|
|
|
|
|
|
|
"github.com/google/uuid"
|
2025-07-17 15:02:18 +08:00
|
|
|
|
"github.com/samber/lo"
|
2025-07-16 21:06:31 +08:00
|
|
|
|
|
2025-08-14 18:04:09 +08:00
|
|
|
|
"github.com/chaitin/panda-wiki/consts"
|
2025-07-16 21:06:31 +08:00
|
|
|
|
"github.com/chaitin/panda-wiki/domain"
|
|
|
|
|
|
"github.com/chaitin/panda-wiki/log"
|
2025-07-17 15:02:18 +08:00
|
|
|
|
"github.com/chaitin/panda-wiki/repo/ipdb"
|
2025-07-16 21:06:31 +08:00
|
|
|
|
"github.com/chaitin/panda-wiki/repo/pg"
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
type CommentUsecase struct {
|
|
|
|
|
|
logger *log.Logger
|
|
|
|
|
|
CommentRepo *pg.CommentRepository
|
|
|
|
|
|
NodeRepo *pg.NodeRepository
|
2025-07-17 15:02:18 +08:00
|
|
|
|
ipRepo *ipdb.IPAddressRepo
|
2025-08-13 16:40:33 +08:00
|
|
|
|
authRepo *pg.AuthRepo
|
2025-07-16 21:06:31 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-17 15:02:18 +08:00
|
|
|
|
func NewCommentUsecase(commentRepo *pg.CommentRepository, logger *log.Logger,
|
2025-08-13 16:40:33 +08:00
|
|
|
|
nodeRepo *pg.NodeRepository, ipRepo *ipdb.IPAddressRepo, authRepo *pg.AuthRepo) *CommentUsecase {
|
2025-07-16 21:06:31 +08:00
|
|
|
|
return &CommentUsecase{
|
|
|
|
|
|
logger: logger.WithModule("usecase.comment"),
|
|
|
|
|
|
CommentRepo: commentRepo,
|
|
|
|
|
|
NodeRepo: nodeRepo,
|
2025-07-17 15:02:18 +08:00
|
|
|
|
ipRepo: ipRepo,
|
2025-08-13 16:40:33 +08:00
|
|
|
|
authRepo: authRepo,
|
2025-07-16 21:06:31 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-08-13 16:40:33 +08:00
|
|
|
|
func (u *CommentUsecase) CreateComment(ctx context.Context, commentReq *domain.CommentReq, KbID string, remoteIP string,
|
|
|
|
|
|
status domain.CommentStatus, userID uint) (string, error) {
|
2025-07-16 21:06:31 +08:00
|
|
|
|
// node
|
|
|
|
|
|
if _, err := u.NodeRepo.GetNodeByID(ctx, commentReq.NodeID); err != nil {
|
|
|
|
|
|
return "", err
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 构造结构体给下方数据库进行插入
|
|
|
|
|
|
CommentID, err := uuid.NewV7()
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return "", err
|
|
|
|
|
|
}
|
|
|
|
|
|
CommentStr := CommentID.String()
|
|
|
|
|
|
|
|
|
|
|
|
err = u.CommentRepo.CreateComment(ctx, &domain.Comment{
|
2025-10-22 18:34:49 +08:00
|
|
|
|
ID: CommentStr,
|
|
|
|
|
|
PicUrls: commentReq.PicUrls,
|
|
|
|
|
|
NodeID: commentReq.NodeID,
|
2025-07-16 21:06:31 +08:00
|
|
|
|
Info: domain.CommentInfo{
|
2025-08-13 16:40:33 +08:00
|
|
|
|
UserName: commentReq.UserName,
|
|
|
|
|
|
RemoteIP: remoteIP,
|
|
|
|
|
|
AuthUserID: userID, // default = 0. have no auth info
|
2025-07-16 21:06:31 +08:00
|
|
|
|
},
|
|
|
|
|
|
ParentID: commentReq.ParentID,
|
|
|
|
|
|
RootID: commentReq.RootID,
|
|
|
|
|
|
Content: commentReq.Content,
|
|
|
|
|
|
CreatedAt: time.Now(),
|
|
|
|
|
|
KbID: KbID,
|
2025-07-25 17:33:03 +08:00
|
|
|
|
Status: status,
|
2025-07-16 21:06:31 +08:00
|
|
|
|
})
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return "", err
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// success
|
|
|
|
|
|
return CommentStr, nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-08-14 18:04:09 +08:00
|
|
|
|
func (u *CommentUsecase) GetCommentListByNodeID(ctx context.Context, nodeID string, edition consts.LicenseEdition) (*domain.PaginatedResult[[]*domain.ShareCommentListItem], error) {
|
2025-07-25 17:33:03 +08:00
|
|
|
|
comments, total, err := u.CommentRepo.GetCommentList(ctx, nodeID, edition)
|
2025-07-16 21:06:31 +08:00
|
|
|
|
if err != nil {
|
|
|
|
|
|
return nil, err
|
|
|
|
|
|
}
|
2025-08-13 16:40:33 +08:00
|
|
|
|
// get auth userinfo --> auth_user_id is not 0
|
|
|
|
|
|
authIDs := make([]uint, 0, len(comments))
|
|
|
|
|
|
for _, comment := range comments {
|
|
|
|
|
|
if comment.Info.AuthUserID != 0 {
|
|
|
|
|
|
authIDs = append(authIDs, comment.Info.AuthUserID)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
// get user info according authIDs
|
|
|
|
|
|
authMap, err := u.authRepo.GetAuthUserinfoByIDs(ctx, authIDs)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
u.logger.Error("get user info failed", log.Error(err))
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-17 20:10:12 +08:00
|
|
|
|
// get ip address
|
|
|
|
|
|
ipAddressMap := make(map[string]*domain.IPAddress)
|
|
|
|
|
|
lo.Map(comments, func(comment *domain.ShareCommentListItem, _ int) *domain.ShareCommentListItem {
|
|
|
|
|
|
if _, ok := ipAddressMap[comment.Info.RemoteIP]; !ok {
|
|
|
|
|
|
ipAddress, err := u.ipRepo.GetIPAddress(ctx, comment.Info.RemoteIP)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
u.logger.Error("get ip address failed", log.Error(err), log.String("ip", comment.Info.RemoteIP))
|
|
|
|
|
|
return comment
|
|
|
|
|
|
}
|
|
|
|
|
|
ipAddressMap[comment.Info.RemoteIP] = ipAddress
|
|
|
|
|
|
comment.IPAddress = ipAddress
|
|
|
|
|
|
comment.Info.RemoteIP = maskIP(comment.Info.RemoteIP)
|
2025-07-17 20:47:41 +08:00
|
|
|
|
comment.IPAddress.IP = maskIP(comment.IPAddress.IP)
|
2025-07-17 20:10:12 +08:00
|
|
|
|
} else {
|
|
|
|
|
|
comment.IPAddress = ipAddressMap[comment.Info.RemoteIP]
|
|
|
|
|
|
}
|
2025-08-13 16:40:33 +08:00
|
|
|
|
if _, ok := authMap[comment.Info.AuthUserID]; ok { // moderate userinfo
|
|
|
|
|
|
comment.Info.UserName = authMap[comment.Info.AuthUserID].AuthUserInfo.Username
|
|
|
|
|
|
comment.Info.Avatar = authMap[comment.Info.AuthUserID].AuthUserInfo.AvatarUrl
|
|
|
|
|
|
comment.Info.Email = authMap[comment.Info.AuthUserID].AuthUserInfo.Email
|
|
|
|
|
|
}
|
2025-07-17 20:10:12 +08:00
|
|
|
|
return comment
|
|
|
|
|
|
})
|
2025-07-18 15:35:43 +08:00
|
|
|
|
// success
|
2025-07-16 21:06:31 +08:00
|
|
|
|
return domain.NewPaginatedResult(comments, uint64(total)), nil
|
|
|
|
|
|
}
|
2025-07-17 15:02:18 +08:00
|
|
|
|
|
2025-08-14 18:04:09 +08:00
|
|
|
|
func (u *CommentUsecase) GetCommentListByKbID(ctx context.Context, req *domain.CommentListReq, edition consts.LicenseEdition) (*domain.PaginatedResult[[]*domain.CommentListItem], error) {
|
2025-07-25 17:33:03 +08:00
|
|
|
|
comments, total, err := u.CommentRepo.GetCommentListByKbID(ctx, req, edition)
|
2025-07-17 15:02:18 +08:00
|
|
|
|
if err != nil {
|
|
|
|
|
|
return nil, err
|
|
|
|
|
|
}
|
2025-08-13 16:40:33 +08:00
|
|
|
|
// get auth userinfo --> auth_user_id is not 0
|
|
|
|
|
|
authIDs := make([]uint, 0, len(comments))
|
|
|
|
|
|
for _, comment := range comments {
|
|
|
|
|
|
if comment.Info.AuthUserID != 0 {
|
|
|
|
|
|
authIDs = append(authIDs, comment.Info.AuthUserID)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
// get user info according authIDs
|
|
|
|
|
|
authMap, err := u.authRepo.GetAuthUserinfoByIDs(ctx, authIDs)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
u.logger.Error("get user info failed", log.Error(err))
|
|
|
|
|
|
}
|
2025-07-17 15:02:18 +08:00
|
|
|
|
// get ip address
|
|
|
|
|
|
ipAddressMap := make(map[string]*domain.IPAddress)
|
|
|
|
|
|
lo.Map(comments, func(comment *domain.CommentListItem, _ int) *domain.CommentListItem {
|
|
|
|
|
|
if _, ok := ipAddressMap[comment.Info.RemoteIP]; !ok {
|
|
|
|
|
|
ipAddress, err := u.ipRepo.GetIPAddress(ctx, comment.Info.RemoteIP)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
u.logger.Error("get ip address failed", log.Error(err), log.String("ip", comment.Info.RemoteIP))
|
|
|
|
|
|
return comment
|
|
|
|
|
|
}
|
|
|
|
|
|
ipAddressMap[comment.Info.RemoteIP] = ipAddress
|
|
|
|
|
|
comment.IPAddress = ipAddress
|
|
|
|
|
|
} else {
|
|
|
|
|
|
comment.IPAddress = ipAddressMap[comment.Info.RemoteIP]
|
|
|
|
|
|
}
|
2025-08-13 16:40:33 +08:00
|
|
|
|
if _, ok := authMap[comment.Info.AuthUserID]; ok { // moderate userinfo
|
|
|
|
|
|
comment.Info.UserName = authMap[comment.Info.AuthUserID].AuthUserInfo.Username
|
|
|
|
|
|
comment.Info.Avatar = authMap[comment.Info.AuthUserID].AuthUserInfo.AvatarUrl
|
|
|
|
|
|
comment.Info.Email = authMap[comment.Info.AuthUserID].AuthUserInfo.Email
|
|
|
|
|
|
}
|
2025-07-17 15:02:18 +08:00
|
|
|
|
return comment
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
return domain.NewPaginatedResult(comments, uint64(total)), nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 批量删除评论, (简单化,只删除传入评论id)
|
|
|
|
|
|
func (u *CommentUsecase) DeleteCommentList(ctx context.Context, req *domain.DeleteCommentListReq) error {
|
|
|
|
|
|
err := u.CommentRepo.DeleteCommentList(ctx, req.IDS)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return err
|
|
|
|
|
|
}
|
|
|
|
|
|
return nil
|
|
|
|
|
|
}
|
2025-07-17 20:10:12 +08:00
|
|
|
|
|
|
|
|
|
|
func maskIP(ip string) string {
|
|
|
|
|
|
if ip == "" {
|
|
|
|
|
|
return ""
|
|
|
|
|
|
}
|
|
|
|
|
|
// 处理 IPv4 地址 (格式: a.b.c.d)
|
|
|
|
|
|
if strings.Contains(ip, ".") {
|
|
|
|
|
|
parts := strings.Split(ip, ".")
|
|
|
|
|
|
if len(parts) != 4 { // 非标准IPv4格式直接返回原值
|
|
|
|
|
|
return ""
|
|
|
|
|
|
}
|
|
|
|
|
|
return parts[0] + ".*.*." + parts[3]
|
|
|
|
|
|
}
|
|
|
|
|
|
// 处理 IPv6 地址 (标准格式包含冒号)
|
|
|
|
|
|
if strings.Contains(ip, ":") {
|
|
|
|
|
|
return ""
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return ""
|
|
|
|
|
|
}
|