This commit is contained in:
2026-02-23 23:50:04 +08:00
commit 084d3b0faf
45 changed files with 4090 additions and 0 deletions

View File

@@ -0,0 +1,98 @@
package services
import (
"lv8girl/internal/models"
"lv8girl/internal/repositories"
)
type AdminService struct {
userRepo *repositories.UserRepository
discussionRepo *repositories.DiscussionRepository
commentRepo *repositories.CommentRepository
likeRepo *repositories.LikeRepository
}
func NewAdminService() *AdminService {
return &AdminService{
userRepo: repositories.NewUserRepository(),
discussionRepo: repositories.NewDiscussionRepository(),
commentRepo: repositories.NewCommentRepository(),
likeRepo: repositories.NewLikeRepository(),
}
}
type Stats struct {
Posts int64
Users int64
Comments int64
Likes int64
Online int64
Approved int64
Rejected int64
Pending int64
}
func (s *AdminService) GetStats() (*Stats, error) {
stats := &Stats{}
stats.Posts, _ = s.discussionRepo.Count()
stats.Users, _ = s.userRepo.Count()
stats.Comments, _ = s.commentRepo.Count()
stats.Likes, _ = s.likeRepo.Count()
stats.Online, _ = s.userRepo.CountOnline()
stats.Approved, _ = s.discussionRepo.CountByStatus("approved")
stats.Rejected, _ = s.discussionRepo.CountByStatus("rejected")
stats.Pending, _ = s.discussionRepo.CountByStatus("pending")
return stats, nil
}
func (s *AdminService) GetPendingPosts() ([]models.Discussion, error) {
return s.discussionRepo.FindPending()
}
func (s *AdminService) GetPendingUsers() ([]models.User, error) {
return s.userRepo.FindPending()
}
func (s *AdminService) ApprovePost(id uint) error {
return s.discussionRepo.UpdateStatus(id, "approved")
}
func (s *AdminService) RejectPost(id uint) error {
return s.discussionRepo.UpdateStatus(id, "rejected")
}
func (s *AdminService) DeletePost(id uint) error {
return s.discussionRepo.Delete(id)
}
func (s *AdminService) ApproveUser(id uint) error {
return s.userRepo.UpdateField(id, "status", "approved")
}
func (s *AdminService) RejectUser(id uint) error {
return s.userRepo.UpdateField(id, "status", "rejected")
}
func (s *AdminService) UpdateUserRole(id uint, role string) error {
return s.userRepo.UpdateField(id, "role", role)
}
func (s *AdminService) DeleteUser(id uint) error {
return s.userRepo.Delete(id)
}
func (s *AdminService) GetAllPosts() ([]models.Discussion, error) {
return s.discussionRepo.FindAll()
}
func (s *AdminService) GetAllUsers() ([]models.User, error) {
return s.userRepo.FindAll()
}
func (s *AdminService) GetAllComments() ([]models.Comment, error) {
return s.commentRepo.FindAll()
}
func (s *AdminService) DeleteComment(id uint) error {
return s.commentRepo.Delete(id)
}

87
internal/services/auth.go Normal file
View File

@@ -0,0 +1,87 @@
package services
import (
"lv8girl/internal/models"
"lv8girl/internal/repositories"
)
type AuthService struct {
userRepo *repositories.UserRepository
}
func NewAuthService() *AuthService {
return &AuthService{
userRepo: repositories.NewUserRepository(),
}
}
type LoginResult struct {
Success bool
User *models.User
Error string
}
func (s *AuthService) Login(login, password string) LoginResult {
user, err := s.userRepo.FindByUsernameOrEmail(login)
if err != nil {
return LoginResult{Success: false, Error: "用户名/邮箱或密码错误"}
}
if !s.userRepo.CheckPassword(password, user.PasswordHash) {
return LoginResult{Success: false, Error: "用户名/邮箱或密码错误"}
}
switch user.Status {
case "pending":
return LoginResult{Success: false, Error: "您的账号正在等待管理员审核,请耐心等待。"}
case "rejected":
return LoginResult{Success: false, Error: "您的账号审核未通过,无法登录。如有疑问,请联系管理员。"}
case "approved":
if user.Role == "banned" {
return LoginResult{Success: false, Error: "您的账号已被封禁,请联系管理员"}
}
default:
return LoginResult{Success: false, Error: "账号状态异常,请联系管理员"}
}
s.userRepo.UpdateLastActive(user.ID)
return LoginResult{Success: true, User: user}
}
type RegisterResult struct {
Success bool
Error string
}
func (s *AuthService) Register(username, email, password string) RegisterResult {
if len(username) < 3 || len(username) > 20 {
return RegisterResult{Success: false, Error: "用户名长度必须在3-20个字符之间"}
}
if s.userRepo.ExistsByUsernameOrEmail(username, email) {
return RegisterResult{Success: false, Error: "用户名或邮箱已被注册"}
}
passwordHash, err := s.userRepo.HashPassword(password)
if err != nil {
return RegisterResult{Success: false, Error: "注册失败,请稍后重试"}
}
user := &models.User{
Username: username,
Email: email,
PasswordHash: passwordHash,
Role: "user",
Status: "pending",
}
if err := s.userRepo.Create(user); err != nil {
return RegisterResult{Success: false, Error: "注册失败,请稍后重试"}
}
return RegisterResult{Success: true}
}
func (s *AuthService) InitAdmin() error {
return s.userRepo.CreateAdminIfNotExists()
}

View File

@@ -0,0 +1,92 @@
package services
import (
"lv8girl/internal/models"
"lv8girl/internal/repositories"
)
type DiscussionService struct {
discussionRepo *repositories.DiscussionRepository
likeRepo *repositories.LikeRepository
commentRepo *repositories.CommentRepository
}
func NewDiscussionService() *DiscussionService {
return &DiscussionService{
discussionRepo: repositories.NewDiscussionRepository(),
likeRepo: repositories.NewLikeRepository(),
commentRepo: repositories.NewCommentRepository(),
}
}
type PostDetailView struct {
Post *models.Discussion
LikeCount int64
CommentCount int64
UserLiked bool
AuthorPostCount int64
}
func (s *DiscussionService) GetPostDetail(postID, userID uint) (*PostDetailView, error) {
post, err := s.discussionRepo.FindByID(postID)
if err != nil {
return nil, err
}
likeCount, _ := s.likeRepo.CountByPostID(postID)
commentCount, _ := s.commentRepo.CountByPostID(postID)
userLiked := s.likeRepo.Exists(postID, userID)
authorPostCount, _ := s.discussionRepo.CountByUserID(post.UserID)
return &PostDetailView{
Post: post,
LikeCount: likeCount,
CommentCount: commentCount,
UserLiked: userLiked,
AuthorPostCount: authorPostCount,
}, nil
}
func (s *DiscussionService) IncrementViews(postID uint) error {
return s.discussionRepo.IncrementViews(postID)
}
func (s *DiscussionService) AddLike(postID, userID uint) error {
if s.likeRepo.Exists(postID, userID) {
return nil
}
like := &models.Like{
PostID: postID,
UserID: userID,
}
return s.likeRepo.Create(like)
}
func (s *DiscussionService) AddComment(postID, userID uint, content string) error {
comment := &models.Comment{
PostID: postID,
UserID: userID,
Content: content,
}
return s.commentRepo.Create(comment)
}
func (s *DiscussionService) GetComments(postID uint) ([]models.Comment, error) {
return s.commentRepo.FindByPostID(postID)
}
func (s *DiscussionService) CreatePost(userID uint, title, content, imagePath string) error {
post := &models.Discussion{
UserID: userID,
Title: title,
Content: content,
ImagePath: imagePath,
Status: "pending",
}
return s.discussionRepo.Create(post)
}
func (s *DiscussionService) GetApprovedPosts(limit int) ([]repositories.PostListItem, error) {
return s.discussionRepo.GetPostList(limit)
}

View File

@@ -0,0 +1,97 @@
package services
import (
"lv8girl/internal/models"
"lv8girl/internal/repositories"
)
type MessageService struct {
messageRepo *repositories.MessageRepository
userRepo *repositories.UserRepository
}
func NewMessageService() *MessageService {
return &MessageService{
messageRepo: repositories.NewMessageRepository(),
userRepo: repositories.NewUserRepository(),
}
}
func (s *MessageService) SendMessage(fromUserID, toUserID uint, content string) error {
message := &models.PrivateMessage{
FromUserID: fromUserID,
ToUserID: toUserID,
Content: content,
IsRead: false,
}
return s.messageRepo.Create(message)
}
func (s *MessageService) GetUnreadCount(userID uint) (int64, error) {
return s.messageRepo.CountUnread(userID)
}
type ConversationView struct {
UserID uint
Username string
Avatar string
LastMsg string
Time string
Unread int64
}
func (s *MessageService) GetConversations(userID uint) ([]ConversationView, error) {
messages, err := s.messageRepo.FindConversations(userID)
if err != nil {
return nil, err
}
userMap := make(map[uint]bool)
for _, msg := range messages {
if msg.FromUserID != userID {
userMap[msg.FromUserID] = true
}
if msg.ToUserID != userID {
userMap[msg.ToUserID] = true
}
}
var conversations []ConversationView
for otherID := range userMap {
otherUser, err := s.userRepo.FindByID(otherID)
if err != nil {
continue
}
lastMsg, _ := s.messageRepo.FindLastMessage(userID, otherID)
unread, _ := s.messageRepo.CountUnreadFromUser(otherID, userID)
avatar := ""
if otherUser.Avatar != "" {
avatar = "/" + otherUser.Avatar
}
conversations = append(conversations, ConversationView{
UserID: otherUser.ID,
Username: otherUser.Username,
Avatar: avatar,
LastMsg: lastMsg.Content,
Time: lastMsg.CreatedAt.Format("2006-01-02 15:04"),
Unread: unread,
})
}
return conversations, nil
}
func (s *MessageService) NotifyUserApproved(adminID, userID uint) error {
return s.SendMessage(adminID, userID, "恭喜!您的账号已通过管理员审核,现在可以正常登录使用了。")
}
func (s *MessageService) NotifyUserRejected(adminID, userID uint) error {
return s.SendMessage(adminID, userID, "您的账号审核未通过。如有疑问,请联系管理员。")
}
func (s *MessageService) NotifyUserBanned(adminID, userID uint) error {
return s.SendMessage(adminID, userID, "您的账号已被管理员封禁。如有疑问,请联系管理员。")
}

94
internal/services/user.go Normal file
View File

@@ -0,0 +1,94 @@
package services
import (
"lv8girl/internal/models"
"lv8girl/internal/repositories"
)
type UserService struct {
userRepo *repositories.UserRepository
discussionRepo *repositories.DiscussionRepository
commentRepo *repositories.CommentRepository
messageRepo *repositories.MessageRepository
}
func NewUserService() *UserService {
return &UserService{
userRepo: repositories.NewUserRepository(),
discussionRepo: repositories.NewDiscussionRepository(),
commentRepo: repositories.NewCommentRepository(),
messageRepo: repositories.NewMessageRepository(),
}
}
type UserProfileView struct {
User *models.User
Posts []models.Discussion
PostCount int64
UnreadCount int64
IsOwner bool
}
func (s *UserService) GetUserProfile(viewerID, targetUserID uint) (*UserProfileView, error) {
user, err := s.userRepo.FindByID(targetUserID)
if err != nil {
return nil, err
}
posts, _ := s.discussionRepo.FindByUserID(targetUserID)
postCount, _ := s.discussionRepo.CountByUserID(targetUserID)
var unreadCount int64
if viewerID != 0 {
unreadCount, _ = s.messageRepo.CountUnread(viewerID)
}
return &UserProfileView{
User: user,
Posts: posts,
PostCount: postCount,
UnreadCount: unreadCount,
IsOwner: viewerID == targetUserID,
}, nil
}
func (s *UserService) UpdateAvatar(userID uint, avatarPath string) error {
user, err := s.userRepo.FindByID(userID)
if err != nil {
return err
}
return s.userRepo.UpdateField(user.ID, "avatar", avatarPath)
}
func (s *UserService) GetUserByID(id uint) (*models.User, error) {
return s.userRepo.FindByID(id)
}
func (s *UserService) UpdateUserStatus(id uint, status string) error {
return s.userRepo.UpdateField(id, "status", status)
}
func (s *UserService) UpdateUserRole(id uint, role string) error {
return s.userRepo.UpdateField(id, "role", role)
}
func (s *UserService) DeleteUser(id uint) error {
return s.userRepo.Delete(id)
}
func (s *UserService) GetPendingUsers() ([]models.User, error) {
return s.userRepo.FindPending()
}
func (s *UserService) GetAllUsers() ([]models.User, error) {
return s.userRepo.FindAll()
}
func (s *UserService) GetUserStats() (int64, int64, error) {
total, err := s.userRepo.Count()
if err != nil {
return 0, 0, err
}
online, err := s.userRepo.CountOnline()
return total, online, err
}