2023-11-01 23:21:12 +03:00
|
|
|
package service
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"errors"
|
|
|
|
"slices"
|
2023-11-12 23:22:58 +03:00
|
|
|
"time"
|
2023-11-01 23:21:12 +03:00
|
|
|
|
|
|
|
"gitrepo.ru/neonxp/nquest/pkg/models"
|
|
|
|
"gorm.io/gorm"
|
|
|
|
"gorm.io/gorm/clause"
|
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
|
|
|
ErrTeamNotFound = errors.New("team not found")
|
|
|
|
)
|
|
|
|
|
|
|
|
type Team struct {
|
2023-11-12 23:22:58 +03:00
|
|
|
DB *gorm.DB
|
|
|
|
User *User
|
2023-11-01 23:21:12 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// NewTeam returns new Team.
|
2023-11-12 23:22:58 +03:00
|
|
|
func NewTeam(db *gorm.DB, user *User) *Team {
|
2023-11-01 23:21:12 +03:00
|
|
|
return &Team{
|
2023-11-12 23:22:58 +03:00
|
|
|
DB: db,
|
|
|
|
User: user,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (ts *Team) GetByID(ctx context.Context, id uint) (*models.Team, error) {
|
|
|
|
t := new(models.Team)
|
|
|
|
|
|
|
|
err := ts.DB.
|
|
|
|
WithContext(ctx).
|
|
|
|
Preload("Members").
|
|
|
|
Preload("Members.User").
|
|
|
|
Preload("Requests").
|
|
|
|
Preload("Requests.User").
|
|
|
|
First(t, id).
|
|
|
|
Error
|
|
|
|
if err != nil {
|
|
|
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
|
|
|
return nil, ErrTeamNotFound
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil, err
|
2023-11-01 23:21:12 +03:00
|
|
|
}
|
2023-11-12 23:22:58 +03:00
|
|
|
|
|
|
|
return t, nil
|
2023-11-01 23:21:12 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
func (ts *Team) List(ctx context.Context) ([]*models.Team, error) {
|
|
|
|
teams := []*models.Team{}
|
|
|
|
|
|
|
|
return teams, ts.DB.WithContext(ctx).Preload("Members").Find(&teams).Error
|
|
|
|
}
|
|
|
|
|
|
|
|
func (ts *Team) Create(ctx context.Context, name string, user *models.User) (*models.Team, error) {
|
|
|
|
t := &models.Team{
|
|
|
|
Name: name,
|
|
|
|
Members: []*models.TeamMember{{
|
|
|
|
User: user,
|
|
|
|
Role: models.Captain,
|
|
|
|
}},
|
|
|
|
}
|
|
|
|
|
|
|
|
db := ts.DB.WithContext(ctx)
|
|
|
|
|
|
|
|
if err := db.Delete(&models.TeamRequest{}, `user_id = ?`, user.ID).Error; err != nil {
|
|
|
|
return t, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return t, db.Create(t).Error
|
|
|
|
}
|
|
|
|
|
2023-11-05 22:22:54 +03:00
|
|
|
func (ts *Team) Delete(ctx context.Context, id uint) error {
|
|
|
|
if err := ts.DB.WithContext(ctx).Delete(&models.Team{}, id).Error; err != nil {
|
|
|
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
|
|
|
return ErrTeamNotFound
|
|
|
|
}
|
|
|
|
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2023-11-12 23:22:58 +03:00
|
|
|
func (ts *Team) Request(ctx context.Context, teamID uint, user *models.User) error {
|
|
|
|
team, err := ts.GetByID(ctx, teamID)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2023-11-01 23:21:12 +03:00
|
|
|
return ts.DB.
|
|
|
|
WithContext(ctx).
|
|
|
|
Clauses(clause.OnConflict{DoNothing: true}).
|
|
|
|
Create(&models.TeamRequest{
|
|
|
|
Team: team,
|
|
|
|
User: user,
|
|
|
|
}).
|
|
|
|
Error
|
|
|
|
}
|
|
|
|
|
2023-11-12 23:22:58 +03:00
|
|
|
func (ts *Team) UpdateMembers(
|
|
|
|
ctx context.Context,
|
|
|
|
teamID uint,
|
|
|
|
newMembers []int,
|
|
|
|
) error {
|
|
|
|
team, err := ts.GetByID(ctx, teamID)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2023-11-01 23:21:12 +03:00
|
|
|
|
2023-11-12 23:22:58 +03:00
|
|
|
newMembersList := make([]*models.TeamMember, 0, len(newMembers))
|
2023-11-01 23:21:12 +03:00
|
|
|
|
2023-11-12 23:22:58 +03:00
|
|
|
for _, tm := range team.Members {
|
|
|
|
idx, ok := slices.BinarySearch(newMembers, int(tm.UserID))
|
|
|
|
if ok {
|
|
|
|
newMembers = slices.Delete(newMembers, idx, idx)
|
|
|
|
newMembersList = append(newMembersList, tm)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if err := ts.DB.WithContext(ctx).Delete(tm).Error; err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2023-11-01 23:21:12 +03:00
|
|
|
}
|
|
|
|
|
2023-11-12 23:22:58 +03:00
|
|
|
for _, userID := range newMembers {
|
|
|
|
_, found := slices.BinarySearchFunc(team.Members, userID, func(tm *models.TeamMember, i int) int {
|
|
|
|
return int(tm.UserID) - i
|
|
|
|
})
|
|
|
|
if found {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
user, err := ts.User.GetUserByID(ctx, uint(userID))
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
newMembersList = append(newMembersList, &models.TeamMember{
|
|
|
|
TeamID: teamID,
|
|
|
|
Team: team,
|
|
|
|
UserID: user.ID,
|
|
|
|
User: user,
|
|
|
|
Role: models.Member,
|
|
|
|
CreatedAt: time.Now(),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
team.Members = newMembersList
|
|
|
|
|
|
|
|
return ts.DB.
|
|
|
|
WithContext(ctx).
|
|
|
|
Session(&gorm.Session{FullSaveAssociations: true}).
|
|
|
|
Updates(team).
|
|
|
|
Error
|
2023-11-01 23:21:12 +03:00
|
|
|
}
|
|
|
|
|
2023-11-12 23:22:58 +03:00
|
|
|
func (ts *Team) ApproveRequest(ctx context.Context, teamID int, userID uint, approve bool) error {
|
|
|
|
team, err := ts.GetByID(ctx, uint(teamID))
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
idx, found := slices.BinarySearchFunc(team.Requests, userID, func(tr *models.TeamRequest, i uint) int {
|
|
|
|
return int(tr.UserID - i)
|
|
|
|
})
|
|
|
|
if !found {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
request := team.Requests[idx]
|
2023-11-01 23:21:12 +03:00
|
|
|
team.Requests = slices.DeleteFunc(team.Requests, func(tr *models.TeamRequest) bool {
|
2023-11-12 23:22:58 +03:00
|
|
|
return tr.UserID == uint(userID)
|
2023-11-01 23:21:12 +03:00
|
|
|
})
|
2023-11-12 23:22:58 +03:00
|
|
|
if err := ts.DB.WithContext(ctx).Delete(request).Error; err != nil {
|
2023-11-01 23:21:12 +03:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2023-11-12 23:22:58 +03:00
|
|
|
if approve {
|
|
|
|
team.Members = append(team.Members, &models.TeamMember{
|
|
|
|
Team: team,
|
|
|
|
TeamID: uint(teamID),
|
|
|
|
UserID: uint(userID),
|
|
|
|
Role: models.Member,
|
|
|
|
CreatedAt: time.Now(),
|
|
|
|
})
|
|
|
|
|
|
|
|
return ts.DB.WithContext(ctx).Save(team).Error
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
2023-11-01 23:21:12 +03:00
|
|
|
}
|
|
|
|
|
2023-11-12 23:22:58 +03:00
|
|
|
func (ts *Team) DeleteMember(ctx context.Context, teamID int, userID uint) error {
|
|
|
|
team, err := ts.GetByID(ctx, uint(teamID))
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
idx, found := slices.BinarySearchFunc(team.Members, userID, func(tm *models.TeamMember, u uint) int {
|
|
|
|
return int(tm.UserID - u)
|
|
|
|
})
|
|
|
|
if !found {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
member := team.Members[idx]
|
|
|
|
|
|
|
|
return ts.DB.WithContext(ctx).Delete(member).Error
|
2023-11-01 23:21:12 +03:00
|
|
|
}
|