feat(group): refactor group invite workflow for improved user experience
- Simplify InviteMembers to always send invite card directly to invitee - Modify RespondInvite to check group join type (anyone vs need approval) before adding member - Update SetGroupAddRequest to properly handle invite flow with invitee agreement and admin approval stages - Add case-insensitive search using LOWER() in user repository - Add random offset (0-50) for first page of recommended posts - Update dependencies: remove gorilla/websocket, promote gomail to direct dependency
This commit is contained in:
3
go.mod
3
go.mod
@@ -7,7 +7,6 @@ require (
|
|||||||
github.com/gin-gonic/gin v1.9.1
|
github.com/gin-gonic/gin v1.9.1
|
||||||
github.com/golang-jwt/jwt/v5 v5.2.0
|
github.com/golang-jwt/jwt/v5 v5.2.0
|
||||||
github.com/google/uuid v1.5.0
|
github.com/google/uuid v1.5.0
|
||||||
github.com/gorilla/websocket v1.5.3
|
|
||||||
github.com/gorse-io/gorse-go v0.5.0-alpha.3
|
github.com/gorse-io/gorse-go v0.5.0-alpha.3
|
||||||
github.com/minio/minio-go/v7 v7.0.66
|
github.com/minio/minio-go/v7 v7.0.66
|
||||||
github.com/redis/go-redis/v9 v9.3.1
|
github.com/redis/go-redis/v9 v9.3.1
|
||||||
@@ -18,6 +17,7 @@ require (
|
|||||||
gorm.io/driver/postgres v1.5.4
|
gorm.io/driver/postgres v1.5.4
|
||||||
gorm.io/driver/sqlite v1.5.4
|
gorm.io/driver/sqlite v1.5.4
|
||||||
gorm.io/gorm v1.25.5
|
gorm.io/gorm v1.25.5
|
||||||
|
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
@@ -80,7 +80,6 @@ require (
|
|||||||
golang.org/x/text v0.22.0 // indirect
|
golang.org/x/text v0.22.0 // indirect
|
||||||
google.golang.org/protobuf v1.31.0 // indirect
|
google.golang.org/protobuf v1.31.0 // indirect
|
||||||
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
|
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
|
||||||
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df // indirect
|
|
||||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -423,7 +423,7 @@ func (h *MessageHandler) HandleSendMessage(c *gin.Context) {
|
|||||||
response.BadRequest(c, "detail_type is required")
|
response.BadRequest(c, "detail_type is required")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if params.Segments == nil || len(params.Segments) == 0 {
|
if len(params.Segments) == 0 {
|
||||||
response.BadRequest(c, "segments is required")
|
response.BadRequest(c, "segments is required")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package repository
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"carrot_bbs/internal/model"
|
"carrot_bbs/internal/model"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
"gorm.io/gorm/clause"
|
"gorm.io/gorm/clause"
|
||||||
@@ -341,8 +342,12 @@ func (r *UserRepository) Search(keyword string, page, pageSize int) ([]*model.Us
|
|||||||
keyword,
|
keyword,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
searchPattern := "%" + keyword + "%"
|
// SQLite: 使用 LOWER() 实现大小写不敏感的模糊搜索
|
||||||
query = query.Where("username LIKE ? OR nickname LIKE ? OR bio LIKE ?", searchPattern, searchPattern, searchPattern)
|
lowerSearchPattern := "%" + strings.ToLower(keyword) + "%"
|
||||||
|
query = query.Where(
|
||||||
|
"LOWER(username) LIKE ? OR LOWER(nickname) LIKE ? OR LOWER(bio) LIKE ?",
|
||||||
|
lowerSearchPattern, lowerSearchPattern, lowerSearchPattern,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -649,7 +649,6 @@ func (s *groupService) InviteMembers(userID string, groupID string, memberIDs []
|
|||||||
if !isInviterMember {
|
if !isInviterMember {
|
||||||
return ErrNotGroupMember
|
return ErrNotGroupMember
|
||||||
}
|
}
|
||||||
isInviterAdmin := s.IsGroupAdmin(userID, groupID)
|
|
||||||
|
|
||||||
inviter, _ := s.userRepo.GetByID(userID)
|
inviter, _ := s.userRepo.GetByID(userID)
|
||||||
inviterName := "群成员"
|
inviterName := "群成员"
|
||||||
@@ -685,7 +684,7 @@ func (s *groupService) InviteMembers(userID string, groupID string, memberIDs []
|
|||||||
|
|
||||||
if _, err := s.requestRepo.GetPendingByGroupAndTarget(groupID, memberID, model.GroupJoinRequestTypeInvite); err == nil {
|
if _, err := s.requestRepo.GetPendingByGroupAndTarget(groupID, memberID, model.GroupJoinRequestTypeInvite); err == nil {
|
||||||
continue
|
continue
|
||||||
} else if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
|
} else if !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
expireAt := time.Now().Add(72 * time.Hour)
|
expireAt := time.Now().Add(72 * time.Hour)
|
||||||
@@ -702,42 +701,9 @@ func (s *groupService) InviteMembers(userID string, groupID string, memberIDs []
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if isInviterAdmin {
|
// 无论是否需要审批,都先发送邀请卡片给被邀请人
|
||||||
// 群主/管理员邀请:直接发送邀请卡片,等待被邀请人确认
|
// 被邀请人同意后,再根据群的策略决定是否需要管理员审批
|
||||||
s.sendGroupInviteToTarget(group, req, inviterName, inviterAvatar)
|
s.sendGroupInviteToTarget(group, req, inviterName, inviterAvatar)
|
||||||
createdCount++
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
inviteeName := "用户"
|
|
||||||
inviteeAvatar := ""
|
|
||||||
if invitee, e := s.userRepo.GetByID(memberID); e == nil && invitee != nil {
|
|
||||||
if invitee.Nickname != "" {
|
|
||||||
inviteeName = invitee.Nickname
|
|
||||||
}
|
|
||||||
inviteeAvatar = invitee.Avatar
|
|
||||||
}
|
|
||||||
reviewerIDs := s.collectGroupReviewerIDs(group.ID, group.OwnerID, userID)
|
|
||||||
for _, reviewerID := range reviewerIDs {
|
|
||||||
s.createSystemNotification(reviewerID, model.SysNotifyGroupJoinApply, inviterName+" 邀请 "+inviteeName+" 加入群聊 "+group.Name+",请审批", &model.SystemNotificationExtra{
|
|
||||||
ActorIDStr: userID,
|
|
||||||
ActorName: inviterName,
|
|
||||||
AvatarURL: inviterAvatar,
|
|
||||||
TargetID: req.Flag,
|
|
||||||
TargetTitle: group.Name,
|
|
||||||
TargetType: string(model.GroupJoinRequestTypeInvite),
|
|
||||||
GroupID: group.ID,
|
|
||||||
GroupName: group.Name,
|
|
||||||
GroupAvatar: group.Avatar,
|
|
||||||
GroupDescription: group.Description,
|
|
||||||
Flag: req.Flag,
|
|
||||||
RequestType: string(req.RequestType),
|
|
||||||
RequestStatus: string(req.Status),
|
|
||||||
TargetUserID: memberID,
|
|
||||||
TargetUserName: inviteeName,
|
|
||||||
TargetUserAvatar: inviteeAvatar,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
createdCount++
|
createdCount++
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -792,7 +758,7 @@ func (s *groupService) JoinGroup(userID string, groupID string) error {
|
|||||||
applicantAvatar,
|
applicantAvatar,
|
||||||
)
|
)
|
||||||
return ErrJoinRequestPending
|
return ErrJoinRequestPending
|
||||||
} else if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
|
} else if !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -861,22 +827,81 @@ func (s *groupService) RespondInvite(userID string, flag string, approve bool, r
|
|||||||
}
|
}
|
||||||
|
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
req.ReviewerID = userID
|
|
||||||
req.ReviewedAt = &now
|
|
||||||
req.Reason = reason
|
req.Reason = reason
|
||||||
|
|
||||||
if approve {
|
if approve {
|
||||||
if err := s.addMemberToGroupAndConversation(group, userID, userID); err != nil {
|
// 检查群的加群策略
|
||||||
return err
|
if group.JoinType == model.JoinTypeAnyone {
|
||||||
|
// 允许任何人加入,直接进群
|
||||||
|
if err := s.addMemberToGroupAndConversation(group, userID, userID); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
req.Status = model.GroupJoinRequestStatusAccepted
|
||||||
|
s.createSystemNotification(req.InitiatorID, model.SysNotifySystem, "你发出的群邀请已被接受", &model.SystemNotificationExtra{
|
||||||
|
GroupID: group.ID,
|
||||||
|
GroupName: group.Name,
|
||||||
|
Flag: req.Flag,
|
||||||
|
RequestType: string(req.RequestType),
|
||||||
|
RequestStatus: string(model.GroupJoinRequestStatusAccepted),
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
// 需要审批或不允许加入时,被邀请人同意后通知管理员审批
|
||||||
|
// 设置 ReviewerID 为被邀请人表示被邀请人已同意
|
||||||
|
req.ReviewerID = userID
|
||||||
|
req.ReviewedAt = &now
|
||||||
|
|
||||||
|
// 获取被邀请人信息
|
||||||
|
targetUserName := "用户"
|
||||||
|
targetUserAvatar := ""
|
||||||
|
if u, e := s.userRepo.GetByID(userID); e == nil && u != nil {
|
||||||
|
if u.Nickname != "" {
|
||||||
|
targetUserName = u.Nickname
|
||||||
|
}
|
||||||
|
targetUserAvatar = u.Avatar
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取邀请人信息
|
||||||
|
inviterName := "群成员"
|
||||||
|
inviterAvatar := ""
|
||||||
|
if u, e := s.userRepo.GetByID(req.InitiatorID); e == nil && u != nil {
|
||||||
|
if u.Nickname != "" {
|
||||||
|
inviterName = u.Nickname
|
||||||
|
}
|
||||||
|
inviterAvatar = u.Avatar
|
||||||
|
}
|
||||||
|
|
||||||
|
// 通知管理员审批
|
||||||
|
reviewerIDs := s.collectGroupReviewerIDs(group.ID, group.OwnerID, req.InitiatorID)
|
||||||
|
for _, reviewerID := range reviewerIDs {
|
||||||
|
s.createSystemNotification(reviewerID, model.SysNotifyGroupJoinApply, inviterName+" 邀请 "+targetUserName+" 加入群聊 "+group.Name+",被邀请人已同意,请审批", &model.SystemNotificationExtra{
|
||||||
|
ActorIDStr: req.InitiatorID,
|
||||||
|
ActorName: inviterName,
|
||||||
|
AvatarURL: inviterAvatar,
|
||||||
|
TargetID: req.Flag,
|
||||||
|
TargetTitle: group.Name,
|
||||||
|
TargetType: string(model.GroupJoinRequestTypeInvite),
|
||||||
|
GroupID: group.ID,
|
||||||
|
GroupName: group.Name,
|
||||||
|
GroupAvatar: group.Avatar,
|
||||||
|
GroupDescription: group.Description,
|
||||||
|
Flag: req.Flag,
|
||||||
|
RequestType: string(req.RequestType),
|
||||||
|
RequestStatus: string(req.Status),
|
||||||
|
TargetUserID: userID,
|
||||||
|
TargetUserName: targetUserName,
|
||||||
|
TargetUserAvatar: targetUserAvatar,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 通知邀请人:被邀请人已同意,等待管理员审批
|
||||||
|
s.createSystemNotification(req.InitiatorID, model.SysNotifySystem, targetUserName+" 已接受你的群邀请,等待管理员审批", &model.SystemNotificationExtra{
|
||||||
|
GroupID: group.ID,
|
||||||
|
GroupName: group.Name,
|
||||||
|
Flag: req.Flag,
|
||||||
|
RequestType: string(req.RequestType),
|
||||||
|
RequestStatus: string(req.Status),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
req.Status = model.GroupJoinRequestStatusAccepted
|
|
||||||
s.createSystemNotification(req.InitiatorID, model.SysNotifySystem, "你发出的群邀请已被接受", &model.SystemNotificationExtra{
|
|
||||||
GroupID: group.ID,
|
|
||||||
GroupName: group.Name,
|
|
||||||
Flag: req.Flag,
|
|
||||||
RequestType: string(req.RequestType),
|
|
||||||
RequestStatus: string(model.GroupJoinRequestStatusAccepted),
|
|
||||||
})
|
|
||||||
} else {
|
} else {
|
||||||
req.Status = model.GroupJoinRequestStatusRejected
|
req.Status = model.GroupJoinRequestStatusRejected
|
||||||
s.createSystemNotification(req.InitiatorID, model.SysNotifySystem, "你发出的群邀请已被拒绝", &model.SystemNotificationExtra{
|
s.createSystemNotification(req.InitiatorID, model.SysNotifySystem, "你发出的群邀请已被拒绝", &model.SystemNotificationExtra{
|
||||||
@@ -906,9 +931,18 @@ func (s *groupService) SetGroupAddRequest(userID string, flag string, approve bo
|
|||||||
if req.Status != model.GroupJoinRequestStatusPending {
|
if req.Status != model.GroupJoinRequestStatusPending {
|
||||||
return ErrGroupRequestHandled
|
return ErrGroupRequestHandled
|
||||||
}
|
}
|
||||||
if req.RequestType == model.GroupJoinRequestTypeInvite && req.ReviewerID != "" {
|
// 检查 invite 类型的请求:
|
||||||
// invite 类型中 reviewerID 非空表示已完成管理员审批,等待被邀请人确认
|
// - ReviewerID 非空表示被邀请人已同意,等待管理员审批
|
||||||
return ErrGroupRequestHandled
|
// - ReviewerID 为空表示被邀请人还未同意,管理员不能提前审批
|
||||||
|
if req.RequestType == model.GroupJoinRequestTypeInvite {
|
||||||
|
if req.ReviewerID == "" {
|
||||||
|
// 被邀请人还未同意,管理员不能提前审批
|
||||||
|
return &ServiceError{Code: 400, Message: "被邀请人尚未同意邀请,无法审批"}
|
||||||
|
}
|
||||||
|
if req.ReviewerID != req.TargetUserID {
|
||||||
|
// ReviewerID 不是被邀请人,说明已经被其他人处理过
|
||||||
|
return ErrGroupRequestHandled
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if !s.IsGroupAdmin(userID, req.GroupID) {
|
if !s.IsGroupAdmin(userID, req.GroupID) {
|
||||||
return ErrNotGroupAdmin
|
return ErrNotGroupAdmin
|
||||||
@@ -923,8 +957,7 @@ func (s *groupService) SetGroupAddRequest(userID string, flag string, approve bo
|
|||||||
}
|
}
|
||||||
|
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
req.ReviewerID = userID
|
// 保存管理员ID到ReviewerID(覆盖被邀请人的ID)
|
||||||
req.ReviewedAt = &now
|
|
||||||
req.Reason = reason
|
req.Reason = reason
|
||||||
targetUserName := "用户"
|
targetUserName := "用户"
|
||||||
if u, e := s.userRepo.GetByID(req.TargetUserID); e == nil && u != nil && u.Nickname != "" {
|
if u, e := s.userRepo.GetByID(req.TargetUserID); e == nil && u != nil && u.Nickname != "" {
|
||||||
@@ -941,29 +974,36 @@ func (s *groupService) SetGroupAddRequest(userID string, flag string, approve bo
|
|||||||
|
|
||||||
if approve {
|
if approve {
|
||||||
if req.RequestType == model.GroupJoinRequestTypeInvite {
|
if req.RequestType == model.GroupJoinRequestTypeInvite {
|
||||||
// 管理员审批通过后,不直接拉人;发送邀请卡片等待对方确认
|
// 被邀请人已同意,管理员审批通过后直接进群
|
||||||
inviterName := "群成员"
|
if err := s.addMemberToGroupAndConversation(group, req.TargetUserID, userID); err != nil {
|
||||||
inviterAvatar := ""
|
return err
|
||||||
if u, e := s.userRepo.GetByID(req.InitiatorID); e == nil && u != nil {
|
|
||||||
if u.Nickname != "" {
|
|
||||||
inviterName = u.Nickname
|
|
||||||
}
|
|
||||||
inviterAvatar = u.Avatar
|
|
||||||
}
|
}
|
||||||
s.sendGroupInviteToTarget(group, req, inviterName, inviterAvatar)
|
req.Status = model.GroupJoinRequestStatusAccepted
|
||||||
s.createSystemNotification(req.InitiatorID, model.SysNotifySystem, "你邀请 "+targetUserName+" 入群已通过审批,等待对方确认", &model.SystemNotificationExtra{
|
req.ReviewerID = userID
|
||||||
|
req.ReviewedAt = &now
|
||||||
|
s.createSystemNotification(req.TargetUserID, model.SysNotifyGroupJoinApproved, "你接受的群邀请已通过审批,你已加入群聊 "+group.Name, &model.SystemNotificationExtra{
|
||||||
GroupID: group.ID,
|
GroupID: group.ID,
|
||||||
GroupName: group.Name,
|
GroupName: group.Name,
|
||||||
GroupAvatar: group.Avatar,
|
GroupAvatar: group.Avatar,
|
||||||
Flag: req.Flag,
|
Flag: req.Flag,
|
||||||
RequestType: string(req.RequestType),
|
RequestType: string(req.RequestType),
|
||||||
RequestStatus: string(model.GroupJoinRequestStatusPending),
|
RequestStatus: string(model.GroupJoinRequestStatusAccepted),
|
||||||
|
})
|
||||||
|
s.createSystemNotification(req.InitiatorID, model.SysNotifySystem, "你邀请 "+targetUserName+" 入群已通过审批", &model.SystemNotificationExtra{
|
||||||
|
GroupID: group.ID,
|
||||||
|
GroupName: group.Name,
|
||||||
|
GroupAvatar: group.Avatar,
|
||||||
|
Flag: req.Flag,
|
||||||
|
RequestType: string(req.RequestType),
|
||||||
|
RequestStatus: string(model.GroupJoinRequestStatusAccepted),
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
if err := s.addMemberToGroupAndConversation(group, req.TargetUserID, userID); err != nil {
|
if err := s.addMemberToGroupAndConversation(group, req.TargetUserID, userID); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
req.Status = model.GroupJoinRequestStatusAccepted
|
req.Status = model.GroupJoinRequestStatusAccepted
|
||||||
|
req.ReviewerID = userID
|
||||||
|
req.ReviewedAt = &now
|
||||||
s.createSystemNotification(req.TargetUserID, model.SysNotifyGroupJoinApproved, "你申请加入群聊 "+group.Name+" 已通过", &model.SystemNotificationExtra{
|
s.createSystemNotification(req.TargetUserID, model.SysNotifyGroupJoinApproved, "你申请加入群聊 "+group.Name+" 已通过", &model.SystemNotificationExtra{
|
||||||
GroupID: group.ID,
|
GroupID: group.ID,
|
||||||
GroupName: group.Name,
|
GroupName: group.Name,
|
||||||
@@ -996,8 +1036,19 @@ func (s *groupService) SetGroupAddRequest(userID string, flag string, approve bo
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
req.Status = model.GroupJoinRequestStatusRejected
|
req.Status = model.GroupJoinRequestStatusRejected
|
||||||
|
req.ReviewerID = userID
|
||||||
|
req.ReviewedAt = &now
|
||||||
if req.RequestType == model.GroupJoinRequestTypeInvite {
|
if req.RequestType == model.GroupJoinRequestTypeInvite {
|
||||||
// 成员邀请被管理员拒绝,仅通知邀请人
|
// 被邀请人已同意但管理员拒绝,通知被邀请人和邀请人
|
||||||
|
s.createSystemNotification(req.TargetUserID, model.SysNotifyGroupJoinRejected, "你接受的群邀请未通过审批", &model.SystemNotificationExtra{
|
||||||
|
GroupID: group.ID,
|
||||||
|
GroupName: group.Name,
|
||||||
|
GroupAvatar: group.Avatar,
|
||||||
|
Flag: req.Flag,
|
||||||
|
RequestType: string(req.RequestType),
|
||||||
|
RequestStatus: string(model.GroupJoinRequestStatusRejected),
|
||||||
|
Reason: reason,
|
||||||
|
})
|
||||||
s.createSystemNotification(req.InitiatorID, model.SysNotifySystem, "你邀请 "+targetUserName+" 入群未通过审批", &model.SystemNotificationExtra{
|
s.createSystemNotification(req.InitiatorID, model.SysNotifySystem, "你邀请 "+targetUserName+" 入群未通过审批", &model.SystemNotificationExtra{
|
||||||
GroupID: group.ID,
|
GroupID: group.ID,
|
||||||
GroupName: group.Name,
|
GroupName: group.Name,
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
|
"math/rand"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -255,7 +256,7 @@ func (s *PostService) List(ctx context.Context, page, pageSize int, userID strin
|
|||||||
}
|
}
|
||||||
cacheKey := cache.PostListKey("latest", visibilityUserKey, page, pageSize)
|
cacheKey := cache.PostListKey("latest", visibilityUserKey, page, pageSize)
|
||||||
|
|
||||||
result, err := cache.GetOrLoadTyped[*PostListResult](
|
result, err := cache.GetOrLoadTyped(
|
||||||
s.cache,
|
s.cache,
|
||||||
cacheKey,
|
cacheKey,
|
||||||
postListTTL,
|
postListTTL,
|
||||||
@@ -595,8 +596,14 @@ func (s *PostService) GetRecommendedPosts(ctx context.Context, userID string, pa
|
|||||||
return s.GetHotPosts(ctx, page, pageSize)
|
return s.GetHotPosts(ctx, page, pageSize)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 计算偏移量
|
// 计算偏移量:第一页使用随机偏移量,增加推荐多样性
|
||||||
offset := (page - 1) * pageSize
|
var offset int
|
||||||
|
if page == 1 {
|
||||||
|
// 第一页随机偏移 0-50,让每次刷新看到不同的推荐内容
|
||||||
|
offset = rand.Intn(51)
|
||||||
|
} else {
|
||||||
|
offset = (page - 1) * pageSize
|
||||||
|
}
|
||||||
|
|
||||||
// 从Gorse获取推荐列表
|
// 从Gorse获取推荐列表
|
||||||
// 多取1条用于判断是否还有下一页
|
// 多取1条用于判断是否还有下一页
|
||||||
|
|||||||
Reference in New Issue
Block a user