Files
backend/internal/service/admin_group_service.go
lan d3065b30cc feat(admin): add comprehensive admin management system
- Add admin handlers and services for users, posts, comments, groups, and dashboard
- Implement role permission management with ability to view and update permissions
- Add database seeding for roles and Casbin permission policies
- Upgrade Casbin from v2 to v3 with GORM adapter for persistent policy storage
- Add admin-specific repository methods with filtering and pagination
- Register new admin API routes under /api/v1/admin/*
2026-03-14 18:01:55 +08:00

391 lines
11 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package service
import (
"context"
"errors"
"carrot_bbs/internal/dto"
"carrot_bbs/internal/model"
"carrot_bbs/internal/repository"
)
// AdminGroupService 管理端群组服务接口
type AdminGroupService interface {
// GetGroupList 获取群组列表(分页、搜索、筛选)
GetGroupList(ctx context.Context, page, pageSize int, keyword, status, ownerID, startDate, endDate string) ([]dto.AdminGroupListResponse, int64, error)
// GetGroupDetail 获取群组详情
GetGroupDetail(ctx context.Context, groupID string) (*dto.AdminGroupDetailResponse, error)
// GetGroupMembers 获取群组成员列表
GetGroupMembers(ctx context.Context, groupID string, page, pageSize int) (*dto.AdminGroupMemberListResponse, error)
// DeleteGroup 解散群组
DeleteGroup(ctx context.Context, groupID string) error
// UpdateGroupStatus 更新群组状态
UpdateGroupStatus(ctx context.Context, groupID string, status string) (*dto.AdminGroupDetailResponse, error)
// RemoveMember 移除群组成员
RemoveMember(ctx context.Context, groupID string, userID string) error
// TransferOwner 转让群主
TransferOwner(ctx context.Context, groupID string, newOwnerID string) (*dto.AdminGroupDetailResponse, error)
}
// adminGroupServiceImpl 管理端群组服务实现
type adminGroupServiceImpl struct {
groupRepo repository.GroupRepository
userRepo *repository.UserRepository
}
// NewAdminGroupService 创建管理端群组服务
func NewAdminGroupService(
groupRepo repository.GroupRepository,
userRepo *repository.UserRepository,
) AdminGroupService {
return &adminGroupServiceImpl{
groupRepo: groupRepo,
userRepo: userRepo,
}
}
// GetGroupList 获取群组列表
func (s *adminGroupServiceImpl) GetGroupList(ctx context.Context, page, pageSize int, keyword, status, ownerID, startDate, endDate string) ([]dto.AdminGroupListResponse, int64, error) {
groups, total, err := s.groupRepo.GetGroupList(page, pageSize, keyword, status, ownerID, startDate, endDate)
if err != nil {
return nil, 0, err
}
responses := make([]dto.AdminGroupListResponse, 0, len(groups))
for _, group := range groups {
response := dto.AdminGroupListResponse{
ID: group.ID,
Name: group.Name,
Avatar: group.Avatar,
Description: group.Description,
OwnerID: group.OwnerID,
MemberCount: group.MemberCount,
MaxMembers: group.MaxMembers,
JoinType: int(group.JoinType),
MuteAll: group.MuteAll,
Status: "active", // 默认状态为active后续可以根据需要添加状态字段
CreatedAt: dto.FormatTime(group.CreatedAt),
UpdatedAt: dto.FormatTime(group.UpdatedAt),
}
// 获取群主信息
if group.OwnerID != "" {
owner, err := s.userRepo.GetByID(group.OwnerID)
if err == nil && owner != nil {
response.Owner = &dto.UserResponse{
ID: owner.ID,
Username: owner.Username,
Nickname: owner.Nickname,
Avatar: owner.Avatar,
CreatedAt: dto.FormatTime(owner.CreatedAt),
}
}
}
// 获取帖子数量
postCount, _ := s.groupRepo.GetGroupPostCount(group.ID)
response.PostCount = postCount
responses = append(responses, response)
}
return responses, total, nil
}
// GetGroupDetail 获取群组详情
func (s *adminGroupServiceImpl) GetGroupDetail(ctx context.Context, groupID string) (*dto.AdminGroupDetailResponse, error) {
group, err := s.groupRepo.GetByID(groupID)
if err != nil {
return nil, err
}
response := &dto.AdminGroupDetailResponse{
ID: group.ID,
Name: group.Name,
Avatar: group.Avatar,
Description: group.Description,
OwnerID: group.OwnerID,
MemberCount: group.MemberCount,
MaxMembers: group.MaxMembers,
JoinType: int(group.JoinType),
MuteAll: group.MuteAll,
Status: "active",
IsPublic: true, // 默认为公开群组,后续可以根据需要添加字段
CreatedAt: dto.FormatTime(group.CreatedAt),
UpdatedAt: dto.FormatTime(group.UpdatedAt),
}
// 获取群主信息
if group.OwnerID != "" {
owner, err := s.userRepo.GetByID(group.OwnerID)
if err == nil && owner != nil {
response.Owner = &dto.UserResponse{
ID: owner.ID,
Username: owner.Username,
Nickname: owner.Nickname,
Email: owner.Email,
Phone: owner.Phone,
Avatar: owner.Avatar,
Bio: owner.Bio,
Website: owner.Website,
Location: owner.Location,
CreatedAt: dto.FormatTime(owner.CreatedAt),
}
}
}
// 获取帖子数量
postCount, _ := s.groupRepo.GetGroupPostCount(group.ID)
response.PostCount = postCount
// 获取最新公告
announcement, _ := s.groupRepo.GetLatestAnnouncement(groupID)
if announcement != nil {
response.Announcement = &dto.GroupAnnouncementResponse{
ID: announcement.ID,
GroupID: announcement.GroupID,
Content: announcement.Content,
AuthorID: announcement.AuthorID,
IsPinned: announcement.IsPinned,
CreatedAt: dto.FormatTime(announcement.CreatedAt),
}
}
return response, nil
}
// GetGroupMembers 获取群组成员列表
func (s *adminGroupServiceImpl) GetGroupMembers(ctx context.Context, groupID string, page, pageSize int) (*dto.AdminGroupMemberListResponse, error) {
// 先检查群组是否存在
_, err := s.groupRepo.GetByID(groupID)
if err != nil {
return nil, errors.New("群组不存在")
}
members, total, err := s.groupRepo.GetMembersWithUserInfo(groupID, page, pageSize)
if err != nil {
return nil, err
}
list := make([]*dto.AdminGroupMemberResponse, 0, len(members))
for _, member := range members {
item := &dto.AdminGroupMemberResponse{
ID: member.ID,
UserID: member.UserID,
Role: member.Role,
Nickname: member.Nickname,
Muted: member.Muted,
JoinedAt: dto.FormatTime(member.JoinTime),
}
if member.User != nil {
item.Username = member.User.Username
item.Nickname = member.User.Nickname
item.Avatar = member.User.Avatar
item.User = &dto.UserResponse{
ID: member.User.ID,
Username: member.User.Username,
Nickname: member.User.Nickname,
Avatar: member.User.Avatar,
CreatedAt: dto.FormatTime(member.User.CreatedAt),
}
}
list = append(list, item)
}
return &dto.AdminGroupMemberListResponse{
List: list,
Total: total,
Page: page,
PageSize: pageSize,
}, nil
}
// DeleteGroup 解散群组
func (s *adminGroupServiceImpl) DeleteGroup(ctx context.Context, groupID string) error {
// 先检查群组是否存在
_, err := s.groupRepo.GetByID(groupID)
if err != nil {
return errors.New("群组不存在")
}
return s.groupRepo.Delete(groupID)
}
// UpdateGroupStatus 更新群组状态
func (s *adminGroupServiceImpl) UpdateGroupStatus(ctx context.Context, groupID string, status string) (*dto.AdminGroupDetailResponse, error) {
// 先检查群组是否存在
group, err := s.groupRepo.GetByID(groupID)
if err != nil {
return nil, errors.New("群组不存在")
}
// 验证状态值
if status != "active" && status != "dissolved" {
return nil, errors.New("无效的状态值")
}
// 如果状态是dissolved则解散群组
if status == "dissolved" {
err = s.groupRepo.Delete(groupID)
if err != nil {
return nil, err
}
} else {
// 更新群组状态
err = s.groupRepo.UpdateGroupStatus(groupID, status)
if err != nil {
return nil, err
}
// 重新获取群组信息
group, err = s.groupRepo.GetByID(groupID)
if err != nil {
return nil, err
}
}
// 构建响应
response := &dto.AdminGroupDetailResponse{
ID: group.ID,
Name: group.Name,
Avatar: group.Avatar,
Description: group.Description,
OwnerID: group.OwnerID,
MemberCount: group.MemberCount,
MaxMembers: group.MaxMembers,
JoinType: int(group.JoinType),
MuteAll: group.MuteAll,
Status: status,
IsPublic: true,
CreatedAt: dto.FormatTime(group.CreatedAt),
UpdatedAt: dto.FormatTime(group.UpdatedAt),
}
// 获取群主信息
if group.OwnerID != "" {
owner, err := s.userRepo.GetByID(group.OwnerID)
if err == nil && owner != nil {
response.Owner = &dto.UserResponse{
ID: owner.ID,
Username: owner.Username,
Nickname: owner.Nickname,
Avatar: owner.Avatar,
CreatedAt: dto.FormatTime(owner.CreatedAt),
}
}
}
return response, nil
}
// RemoveMember 移除群组成员
func (s *adminGroupServiceImpl) RemoveMember(ctx context.Context, groupID string, userID string) error {
// 先检查群组是否存在
_, err := s.groupRepo.GetByID(groupID)
if err != nil {
return errors.New("群组不存在")
}
// 检查用户是否是群成员
member, err := s.groupRepo.GetMember(groupID, userID)
if err != nil {
return errors.New("用户不是群组成员")
}
// 不能移除群主
if member.Role == model.GroupRoleOwner {
return errors.New("不能移除群主")
}
return s.groupRepo.RemoveMember(groupID, userID)
}
// TransferOwner 转让群主
func (s *adminGroupServiceImpl) TransferOwner(ctx context.Context, groupID string, newOwnerID string) (*dto.AdminGroupDetailResponse, error) {
// 先检查群组是否存在
group, err := s.groupRepo.GetByID(groupID)
if err != nil {
return nil, errors.New("群组不存在")
}
// 检查新群主是否是群成员
newOwnerMember, err := s.groupRepo.GetMember(groupID, newOwnerID)
if err != nil {
return nil, errors.New("新群主不是群组成员")
}
// 获取当前群主信息
currentOwnerMember, err := s.groupRepo.GetMember(groupID, group.OwnerID)
if err != nil {
return nil, errors.New("当前群主信息不存在")
}
oldOwnerID := group.OwnerID
// 更新当前群主为普通成员
err = s.groupRepo.SetMemberRole(groupID, oldOwnerID, model.GroupRoleMember)
if err != nil {
return nil, err
}
// 更新新群主角色
err = s.groupRepo.SetMemberRole(groupID, newOwnerID, model.GroupRoleOwner)
if err != nil {
// 回滚
_ = s.groupRepo.SetMemberRole(groupID, oldOwnerID, model.GroupRoleOwner)
return nil, err
}
// 更新群组的OwnerID
group.OwnerID = newOwnerID
err = s.groupRepo.Update(group)
if err != nil {
// 回滚
_ = s.groupRepo.SetMemberRole(groupID, oldOwnerID, model.GroupRoleOwner)
_ = s.groupRepo.SetMemberRole(groupID, newOwnerID, newOwnerMember.Role)
return nil, err
}
// 重新获取群组信息
group, err = s.groupRepo.GetByID(groupID)
if err != nil {
return nil, err
}
// 构建响应
response := &dto.AdminGroupDetailResponse{
ID: group.ID,
Name: group.Name,
Avatar: group.Avatar,
Description: group.Description,
OwnerID: group.OwnerID,
MemberCount: group.MemberCount,
MaxMembers: group.MaxMembers,
JoinType: int(group.JoinType),
MuteAll: group.MuteAll,
Status: "active",
IsPublic: true,
CreatedAt: dto.FormatTime(group.CreatedAt),
UpdatedAt: dto.FormatTime(group.UpdatedAt),
}
// 获取新群主信息
if group.OwnerID != "" {
owner, err := s.userRepo.GetByID(group.OwnerID)
if err == nil && owner != nil {
response.Owner = &dto.UserResponse{
ID: owner.ID,
Username: owner.Username,
Nickname: owner.Nickname,
Avatar: owner.Avatar,
CreatedAt: dto.FormatTime(owner.CreatedAt),
}
}
}
_ = currentOwnerMember // 避免 unused variable 警告
return response, nil
}