2026-04-22 16:01:59 +08:00
|
|
|
|
package dto
|
2026-03-13 09:38:18 +08:00
|
|
|
|
|
|
|
|
|
|
import (
|
2026-04-22 16:01:59 +08:00
|
|
|
|
"with_you/internal/model"
|
|
|
|
|
|
"with_you/internal/pkg/utils"
|
2026-03-13 09:38:18 +08:00
|
|
|
|
)
|
|
|
|
|
|
|
2026-05-04 13:07:03 +08:00
|
|
|
|
type UserResponseOption func(*UserResponse)
|
|
|
|
|
|
|
|
|
|
|
|
func WithFollowing(isFollowing, isFollowingMe bool) UserResponseOption {
|
|
|
|
|
|
return func(r *UserResponse) {
|
|
|
|
|
|
r.IsFollowing = isFollowing
|
|
|
|
|
|
r.IsFollowingMe = isFollowingMe
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func WithPostsCount(count int) UserResponseOption {
|
|
|
|
|
|
return func(r *UserResponse) {
|
|
|
|
|
|
r.PostsCount = count
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-03-13 09:38:18 +08:00
|
|
|
|
|
|
|
|
|
|
// getAvatarOrDefault 获取头像URL,如果为空则返回在线头像生成服务的URL
|
|
|
|
|
|
func getAvatarOrDefault(user *model.User) string {
|
|
|
|
|
|
return utils.GetAvatarOrDefault(user.Username, user.Nickname, user.Avatar)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-26 19:42:10 +08:00
|
|
|
|
// publicVerificationFields 返回认证信息:
|
|
|
|
|
|
// - approved 时返回真实身份和状态
|
|
|
|
|
|
// - 其他状态统一返回 general + none,不暴露具体审核状态
|
2026-04-26 19:38:16 +08:00
|
|
|
|
func publicVerificationFields(user *model.User) (model.UserIdentity, model.VerificationStatus) {
|
|
|
|
|
|
if user.VerificationStatus == model.VerificationStatusApproved {
|
|
|
|
|
|
return user.Identity, user.VerificationStatus
|
|
|
|
|
|
}
|
2026-04-26 19:42:10 +08:00
|
|
|
|
return model.UserIdentityGeneral, model.VerificationStatusNone
|
2026-04-26 19:38:16 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-04 13:07:03 +08:00
|
|
|
|
func baseUserResponse(user *model.User) *UserResponse {
|
2026-03-13 09:38:18 +08:00
|
|
|
|
if user == nil {
|
|
|
|
|
|
return nil
|
|
|
|
|
|
}
|
2026-04-26 19:38:16 +08:00
|
|
|
|
identity, vs := publicVerificationFields(user)
|
2026-03-13 09:38:18 +08:00
|
|
|
|
return &UserResponse{
|
2026-04-26 19:38:16 +08:00
|
|
|
|
ID: user.ID,
|
|
|
|
|
|
Username: user.Username,
|
|
|
|
|
|
Nickname: user.Nickname,
|
|
|
|
|
|
EmailVerified: user.EmailVerified,
|
|
|
|
|
|
Avatar: getAvatarOrDefault(user),
|
|
|
|
|
|
CoverURL: user.CoverURL,
|
|
|
|
|
|
Bio: user.Bio,
|
|
|
|
|
|
Website: user.Website,
|
|
|
|
|
|
Location: user.Location,
|
|
|
|
|
|
PostsCount: user.PostsCount,
|
|
|
|
|
|
FollowersCount: user.FollowersCount,
|
|
|
|
|
|
FollowingCount: user.FollowingCount,
|
|
|
|
|
|
CreatedAt: FormatTime(user.CreatedAt),
|
|
|
|
|
|
Identity: identity,
|
|
|
|
|
|
VerificationStatus: vs,
|
2026-03-19 13:49:51 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-04 13:07:03 +08:00
|
|
|
|
// ConvertUserToResponse 将User转换为UserResponse(公开信息,不包含敏感数据)
|
|
|
|
|
|
func ConvertUserToResponse(user *model.User, opts ...UserResponseOption) *UserResponse {
|
|
|
|
|
|
r := baseUserResponse(user)
|
|
|
|
|
|
if r == nil {
|
2026-03-13 09:38:18 +08:00
|
|
|
|
return nil
|
|
|
|
|
|
}
|
2026-05-04 13:07:03 +08:00
|
|
|
|
for _, opt := range opts {
|
|
|
|
|
|
opt(r)
|
2026-03-13 09:38:18 +08:00
|
|
|
|
}
|
2026-05-04 13:07:03 +08:00
|
|
|
|
return r
|
2026-03-13 09:38:18 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-04 13:07:03 +08:00
|
|
|
|
// ConvertUserToSelfResponse 将User转换为UserSelfResponse(本人信息,包含敏感数据)
|
|
|
|
|
|
func ConvertUserToSelfResponse(user *model.User, postsCount int) *UserSelfResponse {
|
2026-03-13 09:38:18 +08:00
|
|
|
|
if user == nil {
|
|
|
|
|
|
return nil
|
|
|
|
|
|
}
|
2026-05-04 13:07:03 +08:00
|
|
|
|
return &UserSelfResponse{
|
2026-04-26 19:38:16 +08:00
|
|
|
|
ID: user.ID,
|
|
|
|
|
|
Username: user.Username,
|
|
|
|
|
|
Nickname: user.Nickname,
|
|
|
|
|
|
Email: user.Email,
|
2026-05-04 13:07:03 +08:00
|
|
|
|
Phone: user.Phone,
|
2026-04-26 19:38:16 +08:00
|
|
|
|
EmailVerified: user.EmailVerified,
|
|
|
|
|
|
Avatar: getAvatarOrDefault(user),
|
|
|
|
|
|
CoverURL: user.CoverURL,
|
|
|
|
|
|
Bio: user.Bio,
|
|
|
|
|
|
Website: user.Website,
|
|
|
|
|
|
Location: user.Location,
|
2026-05-04 13:07:03 +08:00
|
|
|
|
PostsCount: postsCount,
|
2026-04-26 19:38:16 +08:00
|
|
|
|
FollowersCount: user.FollowersCount,
|
|
|
|
|
|
FollowingCount: user.FollowingCount,
|
|
|
|
|
|
IsVerified: user.IsVerified,
|
|
|
|
|
|
CreatedAt: FormatTime(user.CreatedAt),
|
|
|
|
|
|
PrivacySettings: convertPrivacySettingsToResponse(&user.PrivacySettings),
|
|
|
|
|
|
Identity: user.Identity,
|
|
|
|
|
|
VerificationStatus: user.VerificationStatus,
|
2026-03-13 09:38:18 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-19 13:49:51 +08:00
|
|
|
|
// ConvertUserToDetailResponseWithPostsCount 将User转换为UserDetailResponse(使用实时计算的帖子数量,仅本人可见)
|
2026-03-13 09:38:18 +08:00
|
|
|
|
func ConvertUserToDetailResponseWithPostsCount(user *model.User, postsCount int) *UserDetailResponse {
|
|
|
|
|
|
if user == nil {
|
|
|
|
|
|
return nil
|
|
|
|
|
|
}
|
|
|
|
|
|
return &UserDetailResponse{
|
2026-04-26 19:38:16 +08:00
|
|
|
|
ID: user.ID,
|
|
|
|
|
|
Username: user.Username,
|
|
|
|
|
|
Nickname: user.Nickname,
|
|
|
|
|
|
Email: user.Email,
|
|
|
|
|
|
EmailVerified: user.EmailVerified,
|
|
|
|
|
|
Phone: user.Phone,
|
|
|
|
|
|
Avatar: getAvatarOrDefault(user),
|
|
|
|
|
|
CoverURL: user.CoverURL,
|
|
|
|
|
|
Bio: user.Bio,
|
|
|
|
|
|
Website: user.Website,
|
|
|
|
|
|
Location: user.Location,
|
|
|
|
|
|
PostsCount: postsCount,
|
|
|
|
|
|
FollowersCount: user.FollowersCount,
|
|
|
|
|
|
FollowingCount: user.FollowingCount,
|
|
|
|
|
|
IsVerified: user.IsVerified,
|
|
|
|
|
|
CreatedAt: FormatTime(user.CreatedAt),
|
|
|
|
|
|
PrivacySettings: convertPrivacySettingsToResponse(&user.PrivacySettings),
|
|
|
|
|
|
Identity: user.Identity,
|
|
|
|
|
|
VerificationStatus: user.VerificationStatus,
|
2026-03-13 09:38:18 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-04 13:07:03 +08:00
|
|
|
|
func convertPrivacySettingsToResponse(ps *model.PrivacySettings) *PrivacySettingsResponse {
|
|
|
|
|
|
if ps == nil {
|
|
|
|
|
|
return nil
|
|
|
|
|
|
}
|
|
|
|
|
|
return &PrivacySettingsResponse{
|
|
|
|
|
|
FollowersVisibility: string(ps.FollowersVisibility),
|
|
|
|
|
|
FollowingVisibility: string(ps.FollowingVisibility),
|
|
|
|
|
|
PostsVisibility: string(ps.PostsVisibility),
|
|
|
|
|
|
FavoritesVisibility: string(ps.FavoritesVisibility),
|
2026-03-13 09:38:18 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-04 13:07:03 +08:00
|
|
|
|
// ConvertUsersToResponse 将User列表转换为响应列表
|
|
|
|
|
|
func ConvertUsersToResponse(users []*model.User, opts ...UserResponseOption) []*UserResponse {
|
2026-03-13 09:38:18 +08:00
|
|
|
|
result := make([]*UserResponse, 0, len(users))
|
|
|
|
|
|
for _, user := range users {
|
2026-05-04 13:07:03 +08:00
|
|
|
|
result = append(result, ConvertUserToResponse(user, opts...))
|
2026-03-13 09:38:18 +08:00
|
|
|
|
}
|
|
|
|
|
|
return result
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-04 13:07:03 +08:00
|
|
|
|
// ConvertUsersToResponseWithMutualFollowAndPostsCount 将User列表转换为响应列表(包含双向关注状态和帖子数量)
|
2026-03-13 09:38:18 +08:00
|
|
|
|
// followingStatusMap: key是用户ID,value是[isFollowing, isFollowingMe]
|
|
|
|
|
|
// postsCountMap: key是用户ID,value是帖子数量
|
|
|
|
|
|
func ConvertUsersToResponseWithMutualFollowAndPostsCount(users []*model.User, followingStatusMap map[string][2]bool, postsCountMap map[string]int64) []*UserResponse {
|
|
|
|
|
|
result := make([]*UserResponse, 0, len(users))
|
|
|
|
|
|
for _, user := range users {
|
|
|
|
|
|
status, hasStatus := followingStatusMap[user.ID]
|
|
|
|
|
|
postsCount, hasPostsCount := postsCountMap[user.ID]
|
|
|
|
|
|
if !hasPostsCount {
|
|
|
|
|
|
postsCount = int64(user.PostsCount)
|
|
|
|
|
|
}
|
2026-05-04 13:07:03 +08:00
|
|
|
|
var opts []UserResponseOption
|
2026-03-13 09:38:18 +08:00
|
|
|
|
if hasStatus {
|
2026-05-04 13:07:03 +08:00
|
|
|
|
opts = append(opts, WithFollowing(status[0], status[1]))
|
2026-03-13 09:38:18 +08:00
|
|
|
|
}
|
2026-05-04 13:07:03 +08:00
|
|
|
|
opts = append(opts, WithPostsCount(int(postsCount)))
|
|
|
|
|
|
result = append(result, ConvertUserToResponse(user, opts...))
|
2026-03-13 09:38:18 +08:00
|
|
|
|
}
|
|
|
|
|
|
return result
|
2026-05-04 13:07:03 +08:00
|
|
|
|
}
|