package service import ( "golang.org/x/crypto/bcrypt" "github.com/sundynix/pets-be/internal/model" ) // AdminLogin 校验管理员账号密码。仅允许配置里指定的唯一管理员账号登录后台。 func (s *Service) AdminLogin(username, password string) (*model.Admin, error) { if username != s.cfg.Admin.Username { return nil, ErrNotFound } var admin model.Admin if err := s.db.Where("username = ?", username).First(&admin).Error; err != nil { return nil, ErrNotFound } if admin.Disabled { return nil, ErrNotFound } if err := bcrypt.CompareHashAndPassword([]byte(admin.PasswordHash), []byte(password)); err != nil { return nil, ErrNotFound } return &admin, nil } // GetAdmin 取管理员 func (s *Service) GetAdmin(id string) (*model.Admin, error) { var admin model.Admin if err := s.db.First(&admin, id).Error; err != nil { return nil, ErrNotFound } return &admin, nil } // AdminStats 后台统计 type AdminStats struct { Users int64 `json:"users"` Pets int64 `json:"pets"` Posts int64 `json:"posts"` Records int64 `json:"records"` } // Stats Dashboard 计数 func (s *Service) Stats() (*AdminStats, error) { var st AdminStats s.db.Model(&model.User{}).Count(&st.Users) s.db.Model(&model.Pet{}).Count(&st.Pets) s.db.Model(&model.Post{}).Count(&st.Posts) s.db.Model(&model.HealthRecord{}).Count(&st.Records) return &st, nil } // ListUsers 用户分页(keyword 匹配昵称) func (s *Service) ListUsers(keyword, userType string, offset, limit int) ([]model.User, int64, error) { q := s.db.Model(&model.User{}) if keyword != "" { q = q.Where("nickname LIKE ?", "%"+keyword+"%") } switch userType { case "bot": q = q.Where("is_bot = ?", true) case "real": q = q.Where("is_bot IS NULL OR is_bot = ?", false) } var total int64 q.Count(&total) var users []model.User err := q.Order("id desc").Offset(offset).Limit(limit).Find(&users).Error return users, total, err } // SetUserDisabled 启用/禁用用户 func (s *Service) SetUserDisabled(id string, disabled bool) error { if err := s.db.Model(&model.User{}).Where("id = ?", id).Update("disabled", disabled).Error; err != nil { return err } if disabled { // 顺手吊销刷新令牌,否则禁用后对方还能一直续期 s.RevokeAllRefreshTokens("user", id) } return nil } // ListPetsAdmin 宠物分页 func (s *Service) ListPetsAdmin(offset, limit int) ([]model.Pet, int64, error) { var total int64 s.db.Model(&model.Pet{}).Count(&total) var pets []model.Pet err := s.db.Order("id desc").Offset(offset).Limit(limit).Find(&pets).Error return pets, total, err } // ListPostsAdmin 帖子分页(status 可选过滤) func (s *Service) ListPostsAdmin(status string, offset, limit int) ([]model.Post, int64, error) { q := s.db.Model(&model.Post{}) if status != "" { q = q.Where("status = ?", status) } var total int64 q.Count(&total) var posts []model.Post err := q.Order("id desc").Offset(offset).Limit(limit).Find(&posts).Error return posts, total, err } // SetPostStatus 审核帖子(published/hidden/deleted) func (s *Service) SetPostStatus(id string, status string) error { res := s.db.Model(&model.Post{}).Where("id = ?", id).Update("status", status) if res.Error != nil { return res.Error } if res.RowsAffected == 0 { return ErrNotFound } return nil } // ListCommentsAdmin 评论分页 func (s *Service) ListCommentsAdmin(offset, limit int) ([]model.Comment, int64, error) { var total int64 s.db.Model(&model.Comment{}).Count(&total) var comments []model.Comment err := s.db.Order("id desc").Offset(offset).Limit(limit).Find(&comments).Error return comments, total, err } // DeleteCommentAdmin 删除评论 func (s *Service) DeleteCommentAdmin(id string) error { return s.db.Model(&model.Comment{}).Where("id = ?", id).Update("status", "deleted").Error } // ListArticlesAdmin 文章分页(含未发布) func (s *Service) ListArticlesAdmin(offset, limit int) ([]model.Article, int64, error) { var total int64 s.db.Model(&model.Article{}).Count(&total) var articles []model.Article err := s.db.Order("id desc").Offset(offset).Limit(limit).Find(&articles).Error return articles, total, err } // SaveArticle 新增或更新文章(ID 为 0 则新增) func (s *Service) SaveArticle(a *model.Article) error { if a.ID == "" { return s.db.Create(a).Error } return s.db.Model(&model.Article{}).Where("id = ?", a.ID).Updates(map[string]any{ "icon": a.Icon, "title": a.Title, "description": a.Description, "content": a.Content, "category": a.Category, "related_sheet_type": a.RelatedSheetType, "published": a.Published, }).Error } // DeleteArticle 删除文章 func (s *Service) DeleteArticle(id string) error { res := s.db.Delete(&model.Article{}, id) if res.Error != nil { return res.Error } if res.RowsAffected == 0 { return ErrNotFound } return nil } // ListProAdmin 会员分页 func (s *Service) ListProAdmin(offset, limit int) ([]model.ProMembership, int64, error) { var total int64 s.db.Model(&model.ProMembership{}).Count(&total) var list []model.ProMembership err := s.db.Order("id desc").Offset(offset).Limit(limit).Find(&list).Error return list, total, err }