diff --git a/models/org_team.go b/models/org_team.go
index b6908478c7ea..e808369d29e1 100644
--- a/models/org_team.go
+++ b/models/org_team.go
@@ -7,529 +7,16 @@ package models
 import (
 	"context"
 	"fmt"
-	"strings"
 
 	"code.gitea.io/gitea/models/db"
-	git_model "code.gitea.io/gitea/models/git"
 	issues_model "code.gitea.io/gitea/models/issues"
-	"code.gitea.io/gitea/models/organization"
 	access_model "code.gitea.io/gitea/models/perm/access"
 	repo_model "code.gitea.io/gitea/models/repo"
 	user_model "code.gitea.io/gitea/models/user"
-	"code.gitea.io/gitea/modules/log"
-	"code.gitea.io/gitea/modules/setting"
-	"code.gitea.io/gitea/modules/util"
 
 	"xorm.io/builder"
 )
 
-func AddRepository(ctx context.Context, t *organization.Team, repo *repo_model.Repository) (err error) {
-	if err = organization.AddTeamRepo(ctx, t.OrgID, t.ID, repo.ID); err != nil {
-		return err
-	}
-
-	if err = organization.IncrTeamRepoNum(ctx, t.ID); err != nil {
-		return fmt.Errorf("update team: %w", err)
-	}
-
-	t.NumRepos++
-
-	if err = access_model.RecalculateTeamAccesses(ctx, repo, 0); err != nil {
-		return fmt.Errorf("recalculateAccesses: %w", err)
-	}
-
-	// Make all team members watch this repo if enabled in global settings
-	if setting.Service.AutoWatchNewRepos {
-		if err = t.LoadMembers(ctx); err != nil {
-			return fmt.Errorf("getMembers: %w", err)
-		}
-		for _, u := range t.Members {
-			if err = repo_model.WatchRepo(ctx, u, repo, true); err != nil {
-				return fmt.Errorf("watchRepo: %w", err)
-			}
-		}
-	}
-
-	return nil
-}
-
-// addAllRepositories adds all repositories to the team.
-// If the team already has some repositories they will be left unchanged.
-func addAllRepositories(ctx context.Context, t *organization.Team) error {
-	orgRepos, err := organization.GetOrgRepositories(ctx, t.OrgID)
-	if err != nil {
-		return fmt.Errorf("get org repos: %w", err)
-	}
-
-	for _, repo := range orgRepos {
-		if !organization.HasTeamRepo(ctx, t.OrgID, t.ID, repo.ID) {
-			if err := AddRepository(ctx, t, repo); err != nil {
-				return fmt.Errorf("AddRepository: %w", err)
-			}
-		}
-	}
-
-	return nil
-}
-
-// AddAllRepositories adds all repositories to the team
-func AddAllRepositories(ctx context.Context, t *organization.Team) (err error) {
-	ctx, committer, err := db.TxContext(ctx)
-	if err != nil {
-		return err
-	}
-	defer committer.Close()
-
-	if err = addAllRepositories(ctx, t); err != nil {
-		return err
-	}
-
-	return committer.Commit()
-}
-
-// RemoveAllRepositories removes all repositories from team and recalculates access
-func RemoveAllRepositories(ctx context.Context, t *organization.Team) (err error) {
-	if t.IncludesAllRepositories {
-		return nil
-	}
-
-	ctx, committer, err := db.TxContext(ctx)
-	if err != nil {
-		return err
-	}
-	defer committer.Close()
-
-	if err = removeAllRepositories(ctx, t); err != nil {
-		return err
-	}
-
-	return committer.Commit()
-}
-
-// removeAllRepositories removes all repositories from team and recalculates access
-// Note: Shall not be called if team includes all repositories
-func removeAllRepositories(ctx context.Context, t *organization.Team) (err error) {
-	e := db.GetEngine(ctx)
-	// Delete all accesses.
-	for _, repo := range t.Repos {
-		if err := access_model.RecalculateTeamAccesses(ctx, repo, t.ID); err != nil {
-			return err
-		}
-
-		// Remove watches from all users and now unaccessible repos
-		for _, user := range t.Members {
-			has, err := access_model.HasAnyUnitAccess(ctx, user.ID, repo)
-			if err != nil {
-				return err
-			} else if has {
-				continue
-			}
-
-			if err = repo_model.WatchRepo(ctx, user, repo, false); err != nil {
-				return err
-			}
-
-			// Remove all IssueWatches a user has subscribed to in the repositories
-			if err = issues_model.RemoveIssueWatchersByRepoID(ctx, user.ID, repo.ID); err != nil {
-				return err
-			}
-		}
-	}
-
-	// Delete team-repo
-	if _, err := e.
-		Where("team_id=?", t.ID).
-		Delete(new(organization.TeamRepo)); err != nil {
-		return err
-	}
-
-	t.NumRepos = 0
-	if _, err = e.ID(t.ID).Cols("num_repos").Update(t); err != nil {
-		return err
-	}
-
-	return nil
-}
-
-// NewTeam creates a record of new team.
-// It's caller's responsibility to assign organization ID.
-func NewTeam(ctx context.Context, t *organization.Team) (err error) {
-	if len(t.Name) == 0 {
-		return util.NewInvalidArgumentErrorf("empty team name")
-	}
-
-	if err = organization.IsUsableTeamName(t.Name); err != nil {
-		return err
-	}
-
-	has, err := db.ExistByID[user_model.User](ctx, t.OrgID)
-	if err != nil {
-		return err
-	}
-	if !has {
-		return organization.ErrOrgNotExist{ID: t.OrgID}
-	}
-
-	t.LowerName = strings.ToLower(t.Name)
-	has, err = db.Exist[organization.Team](ctx, builder.Eq{
-		"org_id":     t.OrgID,
-		"lower_name": t.LowerName,
-	})
-	if err != nil {
-		return err
-	}
-	if has {
-		return organization.ErrTeamAlreadyExist{OrgID: t.OrgID, Name: t.LowerName}
-	}
-
-	ctx, committer, err := db.TxContext(ctx)
-	if err != nil {
-		return err
-	}
-	defer committer.Close()
-
-	if err = db.Insert(ctx, t); err != nil {
-		return err
-	}
-
-	// insert units for team
-	if len(t.Units) > 0 {
-		for _, unit := range t.Units {
-			unit.TeamID = t.ID
-		}
-		if err = db.Insert(ctx, &t.Units); err != nil {
-			return err
-		}
-	}
-
-	// Add all repositories to the team if it has access to all of them.
-	if t.IncludesAllRepositories {
-		err = addAllRepositories(ctx, t)
-		if err != nil {
-			return fmt.Errorf("addAllRepositories: %w", err)
-		}
-	}
-
-	// Update organization number of teams.
-	if _, err = db.Exec(ctx, "UPDATE `user` SET num_teams=num_teams+1 WHERE id = ?", t.OrgID); err != nil {
-		return err
-	}
-	return committer.Commit()
-}
-
-// UpdateTeam updates information of team.
-func UpdateTeam(ctx context.Context, t *organization.Team, authChanged, includeAllChanged bool) (err error) {
-	if len(t.Name) == 0 {
-		return util.NewInvalidArgumentErrorf("empty team name")
-	}
-
-	if len(t.Description) > 255 {
-		t.Description = t.Description[:255]
-	}
-
-	ctx, committer, err := db.TxContext(ctx)
-	if err != nil {
-		return err
-	}
-	defer committer.Close()
-
-	t.LowerName = strings.ToLower(t.Name)
-	has, err := db.Exist[organization.Team](ctx, builder.Eq{
-		"org_id":     t.OrgID,
-		"lower_name": t.LowerName,
-	}.And(builder.Neq{"id": t.ID}),
-	)
-	if err != nil {
-		return err
-	} else if has {
-		return organization.ErrTeamAlreadyExist{OrgID: t.OrgID, Name: t.LowerName}
-	}
-
-	sess := db.GetEngine(ctx)
-	if _, err = sess.ID(t.ID).Cols("name", "lower_name", "description",
-		"can_create_org_repo", "authorize", "includes_all_repositories").Update(t); err != nil {
-		return fmt.Errorf("update: %w", err)
-	}
-
-	// update units for team
-	if len(t.Units) > 0 {
-		for _, unit := range t.Units {
-			unit.TeamID = t.ID
-		}
-		// Delete team-unit.
-		if _, err := sess.
-			Where("team_id=?", t.ID).
-			Delete(new(organization.TeamUnit)); err != nil {
-			return err
-		}
-		if _, err = sess.Cols("org_id", "team_id", "type", "access_mode").Insert(&t.Units); err != nil {
-			return err
-		}
-	}
-
-	// Update access for team members if needed.
-	if authChanged {
-		if err = t.LoadRepositories(ctx); err != nil {
-			return fmt.Errorf("LoadRepositories: %w", err)
-		}
-
-		for _, repo := range t.Repos {
-			if err = access_model.RecalculateTeamAccesses(ctx, repo, 0); err != nil {
-				return fmt.Errorf("recalculateTeamAccesses: %w", err)
-			}
-		}
-	}
-
-	// Add all repositories to the team if it has access to all of them.
-	if includeAllChanged && t.IncludesAllRepositories {
-		err = addAllRepositories(ctx, t)
-		if err != nil {
-			return fmt.Errorf("addAllRepositories: %w", err)
-		}
-	}
-
-	return committer.Commit()
-}
-
-// DeleteTeam deletes given team.
-// It's caller's responsibility to assign organization ID.
-func DeleteTeam(ctx context.Context, t *organization.Team) error {
-	ctx, committer, err := db.TxContext(ctx)
-	if err != nil {
-		return err
-	}
-	defer committer.Close()
-
-	if err := t.LoadRepositories(ctx); err != nil {
-		return err
-	}
-
-	if err := t.LoadMembers(ctx); err != nil {
-		return err
-	}
-
-	// update branch protections
-	{
-		protections := make([]*git_model.ProtectedBranch, 0, 10)
-		err := db.GetEngine(ctx).In("repo_id",
-			builder.Select("id").From("repository").Where(builder.Eq{"owner_id": t.OrgID})).
-			Find(&protections)
-		if err != nil {
-			return fmt.Errorf("findProtectedBranches: %w", err)
-		}
-		for _, p := range protections {
-			if err := git_model.RemoveTeamIDFromProtectedBranch(ctx, p, t.ID); err != nil {
-				return err
-			}
-		}
-	}
-
-	if !t.IncludesAllRepositories {
-		if err := removeAllRepositories(ctx, t); err != nil {
-			return err
-		}
-	}
-
-	if err := db.DeleteBeans(ctx,
-		&organization.Team{ID: t.ID},
-		&organization.TeamUser{OrgID: t.OrgID, TeamID: t.ID},
-		&organization.TeamUnit{TeamID: t.ID},
-		&organization.TeamInvite{TeamID: t.ID},
-		&issues_model.Review{Type: issues_model.ReviewTypeRequest, ReviewerTeamID: t.ID}, // batch delete the binding relationship between team and PR (request review from team)
-	); err != nil {
-		return err
-	}
-
-	for _, tm := range t.Members {
-		if err := removeInvalidOrgUser(ctx, t.OrgID, tm); err != nil {
-			return err
-		}
-	}
-
-	// Update organization number of teams.
-	if _, err := db.Exec(ctx, "UPDATE `user` SET num_teams=num_teams-1 WHERE id=?", t.OrgID); err != nil {
-		return err
-	}
-
-	return committer.Commit()
-}
-
-// AddTeamMember adds new membership of given team to given organization,
-// the user will have membership to given organization automatically when needed.
-func AddTeamMember(ctx context.Context, team *organization.Team, user *user_model.User) error {
-	if user_model.IsUserBlockedBy(ctx, user, team.OrgID) {
-		return user_model.ErrBlockedUser
-	}
-
-	isAlreadyMember, err := organization.IsTeamMember(ctx, team.OrgID, team.ID, user.ID)
-	if err != nil || isAlreadyMember {
-		return err
-	}
-
-	if err := organization.AddOrgUser(ctx, team.OrgID, user.ID); err != nil {
-		return err
-	}
-
-	err = db.WithTx(ctx, func(ctx context.Context) error {
-		// check in transaction
-		isAlreadyMember, err = organization.IsTeamMember(ctx, team.OrgID, team.ID, user.ID)
-		if err != nil || isAlreadyMember {
-			return err
-		}
-
-		sess := db.GetEngine(ctx)
-
-		if err := db.Insert(ctx, &organization.TeamUser{
-			UID:    user.ID,
-			OrgID:  team.OrgID,
-			TeamID: team.ID,
-		}); err != nil {
-			return err
-		} else if _, err := sess.Incr("num_members").ID(team.ID).Update(new(organization.Team)); err != nil {
-			return err
-		}
-
-		team.NumMembers++
-
-		// Give access to team repositories.
-		// update exist access if mode become bigger
-		subQuery := builder.Select("repo_id").From("team_repo").
-			Where(builder.Eq{"team_id": team.ID})
-
-		if _, err := sess.Where("user_id=?", user.ID).
-			In("repo_id", subQuery).
-			And("mode < ?", team.AccessMode).
-			SetExpr("mode", team.AccessMode).
-			Update(new(access_model.Access)); err != nil {
-			return fmt.Errorf("update user accesses: %w", err)
-		}
-
-		// for not exist access
-		var repoIDs []int64
-		accessSubQuery := builder.Select("repo_id").From("access").Where(builder.Eq{"user_id": user.ID})
-		if err := sess.SQL(subQuery.And(builder.NotIn("repo_id", accessSubQuery))).Find(&repoIDs); err != nil {
-			return fmt.Errorf("select id accesses: %w", err)
-		}
-
-		accesses := make([]*access_model.Access, 0, 100)
-		for i, repoID := range repoIDs {
-			accesses = append(accesses, &access_model.Access{RepoID: repoID, UserID: user.ID, Mode: team.AccessMode})
-			if (i%100 == 0 || i == len(repoIDs)-1) && len(accesses) > 0 {
-				if err = db.Insert(ctx, accesses); err != nil {
-					return fmt.Errorf("insert new user accesses: %w", err)
-				}
-				accesses = accesses[:0]
-			}
-		}
-		return nil
-	})
-	if err != nil {
-		return err
-	}
-
-	// this behaviour may spend much time so run it in a goroutine
-	// FIXME: Update watch repos batchly
-	if setting.Service.AutoWatchNewRepos {
-		// Get team and its repositories.
-		if err := team.LoadRepositories(ctx); err != nil {
-			log.Error("team.LoadRepositories failed: %v", err)
-		}
-
-		// FIXME: in the goroutine, it can't access the "ctx", it could only use db.DefaultContext at the moment
-		go func(repos []*repo_model.Repository) {
-			for _, repo := range repos {
-				if err = repo_model.WatchRepo(db.DefaultContext, user, repo, true); err != nil {
-					log.Error("watch repo failed: %v", err)
-				}
-			}
-		}(team.Repos)
-	}
-
-	return nil
-}
-
-func removeTeamMember(ctx context.Context, team *organization.Team, user *user_model.User) error {
-	e := db.GetEngine(ctx)
-	isMember, err := organization.IsTeamMember(ctx, team.OrgID, team.ID, user.ID)
-	if err != nil || !isMember {
-		return err
-	}
-
-	// Check if the user to delete is the last member in owner team.
-	if team.IsOwnerTeam() && team.NumMembers == 1 {
-		return organization.ErrLastOrgOwner{UID: user.ID}
-	}
-
-	team.NumMembers--
-
-	if err := team.LoadRepositories(ctx); err != nil {
-		return err
-	}
-
-	if _, err := e.Delete(&organization.TeamUser{
-		UID:    user.ID,
-		OrgID:  team.OrgID,
-		TeamID: team.ID,
-	}); err != nil {
-		return err
-	} else if _, err = e.
-		ID(team.ID).
-		Cols("num_members").
-		Update(team); err != nil {
-		return err
-	}
-
-	// Delete access to team repositories.
-	for _, repo := range team.Repos {
-		if err := access_model.RecalculateUserAccess(ctx, repo, user.ID); err != nil {
-			return err
-		}
-
-		// Remove watches from now unaccessible
-		if err := ReconsiderWatches(ctx, repo, user); err != nil {
-			return err
-		}
-
-		// Remove issue assignments from now unaccessible
-		if err := ReconsiderRepoIssuesAssignee(ctx, repo, user); err != nil {
-			return err
-		}
-	}
-
-	return removeInvalidOrgUser(ctx, team.OrgID, user)
-}
-
-func removeInvalidOrgUser(ctx context.Context, orgID int64, user *user_model.User) error {
-	// Check if the user is a member of any team in the organization.
-	if count, err := db.GetEngine(ctx).Count(&organization.TeamUser{
-		UID:   user.ID,
-		OrgID: orgID,
-	}); err != nil {
-		return err
-	} else if count == 0 {
-		org, err := organization.GetOrgByID(ctx, orgID)
-		if err != nil {
-			return err
-		}
-
-		return RemoveOrgUser(ctx, org, user)
-	}
-	return nil
-}
-
-// RemoveTeamMember removes member from given team of given organization.
-func RemoveTeamMember(ctx context.Context, team *organization.Team, user *user_model.User) error {
-	ctx, committer, err := db.TxContext(ctx)
-	if err != nil {
-		return err
-	}
-	defer committer.Close()
-	if err := removeTeamMember(ctx, team, user); err != nil {
-		return err
-	}
-	return committer.Commit()
-}
-
 func ReconsiderRepoIssuesAssignee(ctx context.Context, repo *repo_model.Repository, user *user_model.User) error {
 	if canAssigned, err := access_model.CanBeAssigned(ctx, user, repo, true); err != nil || canAssigned {
 		return err
diff --git a/models/organization/team_test.go b/models/organization/team_test.go
index 23a6affe2480..8c34e7a61247 100644
--- a/models/organization/team_test.go
+++ b/models/organization/team_test.go
@@ -197,3 +197,8 @@ func TestUsersInTeamsCount(t *testing.T) {
 	test([]int64{1, 2, 3, 4, 5}, []int64{2, 5}, 2)    // userid 2,4
 	test([]int64{1, 2, 3, 4, 5}, []int64{2, 3, 5}, 3) // userid 2,4,5
 }
+
+func TestIsUsableTeamName(t *testing.T) {
+	assert.NoError(t, organization.IsUsableTeamName("usable"))
+	assert.True(t, db.IsErrNameReserved(organization.IsUsableTeamName("new")))
+}
diff --git a/routers/api/v1/org/member.go b/routers/api/v1/org/member.go
index edcee1e20771..294d33014d13 100644
--- a/routers/api/v1/org/member.go
+++ b/routers/api/v1/org/member.go
@@ -7,7 +7,6 @@ import (
 	"net/http"
 	"net/url"
 
-	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/organization"
 	"code.gitea.io/gitea/modules/setting"
 	api "code.gitea.io/gitea/modules/structs"
@@ -15,6 +14,7 @@ import (
 	"code.gitea.io/gitea/routers/api/v1/utils"
 	"code.gitea.io/gitea/services/context"
 	"code.gitea.io/gitea/services/convert"
+	org_service "code.gitea.io/gitea/services/org"
 )
 
 // listMembers list an organization's members
@@ -322,7 +322,7 @@ func DeleteMember(ctx *context.APIContext) {
 	if ctx.Written() {
 		return
 	}
-	if err := models.RemoveOrgUser(ctx, ctx.Org.Organization, member); err != nil {
+	if err := org_service.RemoveOrgUser(ctx, ctx.Org.Organization, member); err != nil {
 		ctx.Error(http.StatusInternalServerError, "RemoveOrgUser", err)
 	}
 	ctx.Status(http.StatusNoContent)
diff --git a/routers/api/v1/org/team.go b/routers/api/v1/org/team.go
index c55837ff44fd..20226b4d6b0e 100644
--- a/routers/api/v1/org/team.go
+++ b/routers/api/v1/org/team.go
@@ -8,7 +8,6 @@ import (
 	"errors"
 	"net/http"
 
-	"code.gitea.io/gitea/models"
 	activities_model "code.gitea.io/gitea/models/activities"
 	"code.gitea.io/gitea/models/organization"
 	"code.gitea.io/gitea/models/perm"
@@ -240,7 +239,7 @@ func CreateTeam(ctx *context.APIContext) {
 		attachAdminTeamUnits(team)
 	}
 
-	if err := models.NewTeam(ctx, team); err != nil {
+	if err := org_service.NewTeam(ctx, team); err != nil {
 		if organization.IsErrTeamAlreadyExist(err) {
 			ctx.Error(http.StatusUnprocessableEntity, "", err)
 		} else {
@@ -331,7 +330,7 @@ func EditTeam(ctx *context.APIContext) {
 		attachAdminTeamUnits(team)
 	}
 
-	if err := models.UpdateTeam(ctx, team, isAuthChanged, isIncludeAllChanged); err != nil {
+	if err := org_service.UpdateTeam(ctx, team, isAuthChanged, isIncludeAllChanged); err != nil {
 		ctx.Error(http.StatusInternalServerError, "EditTeam", err)
 		return
 	}
@@ -362,7 +361,7 @@ func DeleteTeam(ctx *context.APIContext) {
 	//   "404":
 	//     "$ref": "#/responses/notFound"
 
-	if err := models.DeleteTeam(ctx, ctx.Org.Team); err != nil {
+	if err := org_service.DeleteTeam(ctx, ctx.Org.Team); err != nil {
 		ctx.Error(http.StatusInternalServerError, "DeleteTeam", err)
 		return
 	}
@@ -496,7 +495,7 @@ func AddTeamMember(ctx *context.APIContext) {
 	if ctx.Written() {
 		return
 	}
-	if err := models.AddTeamMember(ctx, ctx.Org.Team, u); err != nil {
+	if err := org_service.AddTeamMember(ctx, ctx.Org.Team, u); err != nil {
 		if errors.Is(err, user_model.ErrBlockedUser) {
 			ctx.Error(http.StatusForbidden, "AddTeamMember", err)
 		} else {
@@ -537,7 +536,7 @@ func RemoveTeamMember(ctx *context.APIContext) {
 		return
 	}
 
-	if err := models.RemoveTeamMember(ctx, ctx.Org.Team, u); err != nil {
+	if err := org_service.RemoveTeamMember(ctx, ctx.Org.Team, u); err != nil {
 		ctx.Error(http.StatusInternalServerError, "RemoveTeamMember", err)
 		return
 	}
@@ -700,7 +699,7 @@ func AddTeamRepository(ctx *context.APIContext) {
 		ctx.Error(http.StatusForbidden, "", "Must have admin-level access to the repository")
 		return
 	}
-	if err := org_service.TeamAddRepository(ctx, ctx.Org.Team, repo); err != nil {
+	if err := repo_service.TeamAddRepository(ctx, ctx.Org.Team, repo); err != nil {
 		ctx.Error(http.StatusInternalServerError, "TeamAddRepository", err)
 		return
 	}
diff --git a/routers/api/v1/repo/teams.go b/routers/api/v1/repo/teams.go
index ddd325482da3..82ecaf30201c 100644
--- a/routers/api/v1/repo/teams.go
+++ b/routers/api/v1/repo/teams.go
@@ -10,7 +10,6 @@ import (
 	"code.gitea.io/gitea/models/organization"
 	"code.gitea.io/gitea/services/context"
 	"code.gitea.io/gitea/services/convert"
-	org_service "code.gitea.io/gitea/services/org"
 	repo_service "code.gitea.io/gitea/services/repository"
 )
 
@@ -205,7 +204,7 @@ func changeRepoTeam(ctx *context.APIContext, add bool) {
 			ctx.Error(http.StatusUnprocessableEntity, "alreadyAdded", fmt.Errorf("team '%s' is already added to repo", team.Name))
 			return
 		}
-		err = org_service.TeamAddRepository(ctx, team, ctx.Repo.Repository)
+		err = repo_service.TeamAddRepository(ctx, team, ctx.Repo.Repository)
 	} else {
 		if !repoHasTeam {
 			ctx.Error(http.StatusUnprocessableEntity, "notAdded", fmt.Errorf("team '%s' was not added to repo", team.Name))
diff --git a/routers/web/org/members.go b/routers/web/org/members.go
index 97dfff3afe64..7af087c4df38 100644
--- a/routers/web/org/members.go
+++ b/routers/web/org/members.go
@@ -7,7 +7,6 @@ package org
 import (
 	"net/http"
 
-	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/organization"
 	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/base"
@@ -15,6 +14,7 @@ import (
 	"code.gitea.io/gitea/modules/setting"
 	shared_user "code.gitea.io/gitea/routers/web/shared/user"
 	"code.gitea.io/gitea/services/context"
+	org_service "code.gitea.io/gitea/services/org"
 )
 
 const (
@@ -108,14 +108,14 @@ func MembersAction(ctx *context.Context) {
 			ctx.Error(http.StatusNotFound)
 			return
 		}
-		err = models.RemoveOrgUser(ctx, org, member)
+		err = org_service.RemoveOrgUser(ctx, org, member)
 		if organization.IsErrLastOrgOwner(err) {
 			ctx.Flash.Error(ctx.Tr("form.last_org_owner"))
 			ctx.JSONRedirect(ctx.Org.OrgLink + "/members")
 			return
 		}
 	case "leave":
-		err = models.RemoveOrgUser(ctx, org, ctx.Doer)
+		err = org_service.RemoveOrgUser(ctx, org, ctx.Doer)
 		if err == nil {
 			ctx.Flash.Success(ctx.Tr("form.organization_leave_success", org.DisplayName()))
 			ctx.JSON(http.StatusOK, map[string]any{
diff --git a/routers/web/org/teams.go b/routers/web/org/teams.go
index 31b9601ce79d..bd7883210325 100644
--- a/routers/web/org/teams.go
+++ b/routers/web/org/teams.go
@@ -13,7 +13,6 @@ import (
 	"strconv"
 	"strings"
 
-	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/db"
 	org_model "code.gitea.io/gitea/models/organization"
 	"code.gitea.io/gitea/models/perm"
@@ -78,9 +77,9 @@ func TeamsAction(ctx *context.Context) {
 			ctx.Error(http.StatusNotFound)
 			return
 		}
-		err = models.AddTeamMember(ctx, ctx.Org.Team, ctx.Doer)
+		err = org_service.AddTeamMember(ctx, ctx.Org.Team, ctx.Doer)
 	case "leave":
-		err = models.RemoveTeamMember(ctx, ctx.Org.Team, ctx.Doer)
+		err = org_service.RemoveTeamMember(ctx, ctx.Org.Team, ctx.Doer)
 		if err != nil {
 			if org_model.IsErrLastOrgOwner(err) {
 				ctx.Flash.Error(ctx.Tr("form.last_org_owner"))
@@ -107,7 +106,7 @@ func TeamsAction(ctx *context.Context) {
 			return
 		}
 
-		err = models.RemoveTeamMember(ctx, ctx.Org.Team, user)
+		err = org_service.RemoveTeamMember(ctx, ctx.Org.Team, user)
 		if err != nil {
 			if org_model.IsErrLastOrgOwner(err) {
 				ctx.Flash.Error(ctx.Tr("form.last_org_owner"))
@@ -162,7 +161,7 @@ func TeamsAction(ctx *context.Context) {
 		if ctx.Org.Team.IsMember(ctx, u.ID) {
 			ctx.Flash.Error(ctx.Tr("org.teams.add_duplicate_users"))
 		} else {
-			err = models.AddTeamMember(ctx, ctx.Org.Team, u)
+			err = org_service.AddTeamMember(ctx, ctx.Org.Team, u)
 		}
 
 		page = "team"
@@ -249,13 +248,13 @@ func TeamsRepoAction(ctx *context.Context) {
 			ctx.ServerError("GetRepositoryByName", err)
 			return
 		}
-		err = org_service.TeamAddRepository(ctx, ctx.Org.Team, repo)
+		err = repo_service.TeamAddRepository(ctx, ctx.Org.Team, repo)
 	case "remove":
 		err = repo_service.RemoveRepositoryFromTeam(ctx, ctx.Org.Team, ctx.FormInt64("repoid"))
 	case "addall":
-		err = models.AddAllRepositories(ctx, ctx.Org.Team)
+		err = repo_service.AddAllRepositoriesToTeam(ctx, ctx.Org.Team)
 	case "removeall":
-		err = models.RemoveAllRepositories(ctx, ctx.Org.Team)
+		err = repo_service.RemoveAllRepositoriesFromTeam(ctx, ctx.Org.Team)
 	}
 
 	if err != nil {
@@ -358,7 +357,7 @@ func NewTeamPost(ctx *context.Context) {
 		return
 	}
 
-	if err := models.NewTeam(ctx, t); err != nil {
+	if err := org_service.NewTeam(ctx, t); err != nil {
 		ctx.Data["Err_TeamName"] = true
 		switch {
 		case org_model.IsErrTeamAlreadyExist(err):
@@ -536,7 +535,7 @@ func EditTeamPost(ctx *context.Context) {
 		return
 	}
 
-	if err := models.UpdateTeam(ctx, t, isAuthChanged, isIncludeAllChanged); err != nil {
+	if err := org_service.UpdateTeam(ctx, t, isAuthChanged, isIncludeAllChanged); err != nil {
 		ctx.Data["Err_TeamName"] = true
 		switch {
 		case org_model.IsErrTeamAlreadyExist(err):
@@ -551,7 +550,7 @@ func EditTeamPost(ctx *context.Context) {
 
 // DeleteTeam response for the delete team request
 func DeleteTeam(ctx *context.Context) {
-	if err := models.DeleteTeam(ctx, ctx.Org.Team); err != nil {
+	if err := org_service.DeleteTeam(ctx, ctx.Org.Team); err != nil {
 		ctx.Flash.Error("DeleteTeam: " + err.Error())
 	} else {
 		ctx.Flash.Success(ctx.Tr("org.teams.delete_team_success"))
@@ -593,7 +592,7 @@ func TeamInvitePost(ctx *context.Context) {
 		return
 	}
 
-	if err := models.AddTeamMember(ctx, team, ctx.Doer); err != nil {
+	if err := org_service.AddTeamMember(ctx, team, ctx.Doer); err != nil {
 		ctx.ServerError("AddTeamMember", err)
 		return
 	}
diff --git a/routers/web/repo/setting/collaboration.go b/routers/web/repo/setting/collaboration.go
index 18ecff825085..cdf91edf4a2c 100644
--- a/routers/web/repo/setting/collaboration.go
+++ b/routers/web/repo/setting/collaboration.go
@@ -17,7 +17,6 @@ import (
 	"code.gitea.io/gitea/modules/setting"
 	"code.gitea.io/gitea/services/context"
 	"code.gitea.io/gitea/services/mailer"
-	org_service "code.gitea.io/gitea/services/org"
 	repo_service "code.gitea.io/gitea/services/repository"
 )
 
@@ -185,7 +184,7 @@ func AddTeamPost(ctx *context.Context) {
 		return
 	}
 
-	if err = org_service.TeamAddRepository(ctx, team, ctx.Repo.Repository); err != nil {
+	if err = repo_service.TeamAddRepository(ctx, team, ctx.Repo.Repository); err != nil {
 		ctx.ServerError("TeamAddRepository", err)
 		return
 	}
diff --git a/services/auth/source/source_group_sync.go b/services/auth/source/source_group_sync.go
index 05293f202f5d..9cb7d4165ccd 100644
--- a/services/auth/source/source_group_sync.go
+++ b/services/auth/source/source_group_sync.go
@@ -7,11 +7,11 @@ import (
 	"context"
 	"fmt"
 
-	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/organization"
 	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/container"
 	"code.gitea.io/gitea/modules/log"
+	org_service "code.gitea.io/gitea/services/org"
 )
 
 type syncType int
@@ -100,12 +100,12 @@ func syncGroupsToTeamsCached(ctx context.Context, user *user_model.User, orgTeam
 			}
 
 			if action == syncAdd && !isMember {
-				if err := models.AddTeamMember(ctx, team, user); err != nil {
+				if err := org_service.AddTeamMember(ctx, team, user); err != nil {
 					log.Error("group sync: Could not add user to team: %v", err)
 					return err
 				}
 			} else if action == syncRemove && isMember {
-				if err := models.RemoveTeamMember(ctx, team, user); err != nil {
+				if err := org_service.RemoveTeamMember(ctx, team, user); err != nil {
 					log.Error("group sync: Could not remove user from team: %v", err)
 					return err
 				}
diff --git a/services/doctor/fix8312.go b/services/doctor/fix8312.go
index 4fc049873adb..3e2ca68eb42f 100644
--- a/services/doctor/fix8312.go
+++ b/services/doctor/fix8312.go
@@ -6,11 +6,11 @@ package doctor
 import (
 	"context"
 
-	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/db"
 	org_model "code.gitea.io/gitea/models/organization"
 	"code.gitea.io/gitea/models/perm"
 	"code.gitea.io/gitea/modules/log"
+	org_service "code.gitea.io/gitea/services/org"
 
 	"xorm.io/builder"
 )
@@ -29,7 +29,7 @@ func fixOwnerTeamCreateOrgRepo(ctx context.Context, logger log.Logger, autofix b
 				return nil
 			}
 
-			return models.UpdateTeam(ctx, team, false, false)
+			return org_service.UpdateTeam(ctx, team, false, false)
 		},
 	)
 	if err != nil {
diff --git a/services/org/repo.go b/services/org/repo.go
deleted file mode 100644
index 78a829ef2595..000000000000
--- a/services/org/repo.go
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright 2022 The Gitea Authors. All rights reserved.
-// SPDX-License-Identifier: MIT
-
-package org
-
-import (
-	"context"
-	"errors"
-
-	"code.gitea.io/gitea/models"
-	"code.gitea.io/gitea/models/db"
-	"code.gitea.io/gitea/models/organization"
-	repo_model "code.gitea.io/gitea/models/repo"
-)
-
-// TeamAddRepository adds new repository to team of organization.
-func TeamAddRepository(ctx context.Context, t *organization.Team, repo *repo_model.Repository) (err error) {
-	if repo.OwnerID != t.OrgID {
-		return errors.New("repository does not belong to organization")
-	} else if organization.HasTeamRepo(ctx, t.OrgID, t.ID, repo.ID) {
-		return nil
-	}
-
-	return db.WithTx(ctx, func(ctx context.Context) error {
-		return models.AddRepository(ctx, t, repo)
-	})
-}
diff --git a/services/org/team.go b/services/org/team.go
new file mode 100644
index 000000000000..16675593a795
--- /dev/null
+++ b/services/org/team.go
@@ -0,0 +1,402 @@
+// Copyright 2024 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package org
+
+import (
+	"context"
+	"fmt"
+	"strings"
+
+	"code.gitea.io/gitea/models"
+	"code.gitea.io/gitea/models/db"
+	git_model "code.gitea.io/gitea/models/git"
+	issues_model "code.gitea.io/gitea/models/issues"
+	"code.gitea.io/gitea/models/organization"
+	access_model "code.gitea.io/gitea/models/perm/access"
+	repo_model "code.gitea.io/gitea/models/repo"
+	user_model "code.gitea.io/gitea/models/user"
+	"code.gitea.io/gitea/modules/log"
+	"code.gitea.io/gitea/modules/setting"
+	"code.gitea.io/gitea/modules/util"
+	repo_service "code.gitea.io/gitea/services/repository"
+
+	"xorm.io/builder"
+)
+
+// NewTeam creates a record of new team.
+// It's caller's responsibility to assign organization ID.
+func NewTeam(ctx context.Context, t *organization.Team) (err error) {
+	if len(t.Name) == 0 {
+		return util.NewInvalidArgumentErrorf("empty team name")
+	}
+
+	if err = organization.IsUsableTeamName(t.Name); err != nil {
+		return err
+	}
+
+	has, err := db.ExistByID[user_model.User](ctx, t.OrgID)
+	if err != nil {
+		return err
+	}
+	if !has {
+		return organization.ErrOrgNotExist{ID: t.OrgID}
+	}
+
+	t.LowerName = strings.ToLower(t.Name)
+	has, err = db.Exist[organization.Team](ctx, builder.Eq{
+		"org_id":     t.OrgID,
+		"lower_name": t.LowerName,
+	})
+	if err != nil {
+		return err
+	}
+	if has {
+		return organization.ErrTeamAlreadyExist{OrgID: t.OrgID, Name: t.LowerName}
+	}
+
+	ctx, committer, err := db.TxContext(ctx)
+	if err != nil {
+		return err
+	}
+	defer committer.Close()
+
+	if err = db.Insert(ctx, t); err != nil {
+		return err
+	}
+
+	// insert units for team
+	if len(t.Units) > 0 {
+		for _, unit := range t.Units {
+			unit.TeamID = t.ID
+		}
+		if err = db.Insert(ctx, &t.Units); err != nil {
+			return err
+		}
+	}
+
+	// Add all repositories to the team if it has access to all of them.
+	if t.IncludesAllRepositories {
+		err = repo_service.AddAllRepositoriesToTeam(ctx, t)
+		if err != nil {
+			return fmt.Errorf("addAllRepositories: %w", err)
+		}
+	}
+
+	// Update organization number of teams.
+	if _, err = db.Exec(ctx, "UPDATE `user` SET num_teams=num_teams+1 WHERE id = ?", t.OrgID); err != nil {
+		return err
+	}
+	return committer.Commit()
+}
+
+// UpdateTeam updates information of team.
+func UpdateTeam(ctx context.Context, t *organization.Team, authChanged, includeAllChanged bool) (err error) {
+	if len(t.Name) == 0 {
+		return util.NewInvalidArgumentErrorf("empty team name")
+	}
+
+	if len(t.Description) > 255 {
+		t.Description = t.Description[:255]
+	}
+
+	ctx, committer, err := db.TxContext(ctx)
+	if err != nil {
+		return err
+	}
+	defer committer.Close()
+
+	t.LowerName = strings.ToLower(t.Name)
+	has, err := db.Exist[organization.Team](ctx, builder.Eq{
+		"org_id":     t.OrgID,
+		"lower_name": t.LowerName,
+	}.And(builder.Neq{"id": t.ID}),
+	)
+	if err != nil {
+		return err
+	} else if has {
+		return organization.ErrTeamAlreadyExist{OrgID: t.OrgID, Name: t.LowerName}
+	}
+
+	sess := db.GetEngine(ctx)
+	if _, err = sess.ID(t.ID).Cols("name", "lower_name", "description",
+		"can_create_org_repo", "authorize", "includes_all_repositories").Update(t); err != nil {
+		return fmt.Errorf("update: %w", err)
+	}
+
+	// update units for team
+	if len(t.Units) > 0 {
+		for _, unit := range t.Units {
+			unit.TeamID = t.ID
+		}
+		// Delete team-unit.
+		if _, err := sess.
+			Where("team_id=?", t.ID).
+			Delete(new(organization.TeamUnit)); err != nil {
+			return err
+		}
+		if _, err = sess.Cols("org_id", "team_id", "type", "access_mode").Insert(&t.Units); err != nil {
+			return err
+		}
+	}
+
+	// Update access for team members if needed.
+	if authChanged {
+		if err = t.LoadRepositories(ctx); err != nil {
+			return fmt.Errorf("LoadRepositories: %w", err)
+		}
+
+		for _, repo := range t.Repos {
+			if err = access_model.RecalculateTeamAccesses(ctx, repo, 0); err != nil {
+				return fmt.Errorf("recalculateTeamAccesses: %w", err)
+			}
+		}
+	}
+
+	// Add all repositories to the team if it has access to all of them.
+	if includeAllChanged && t.IncludesAllRepositories {
+		err = repo_service.AddAllRepositoriesToTeam(ctx, t)
+		if err != nil {
+			return fmt.Errorf("addAllRepositories: %w", err)
+		}
+	}
+
+	return committer.Commit()
+}
+
+// AddTeamMember adds new membership of given team to given organization,
+// the user will have membership to given organization automatically when needed.
+func AddTeamMember(ctx context.Context, team *organization.Team, user *user_model.User) error {
+	if user_model.IsUserBlockedBy(ctx, user, team.OrgID) {
+		return user_model.ErrBlockedUser
+	}
+
+	isAlreadyMember, err := organization.IsTeamMember(ctx, team.OrgID, team.ID, user.ID)
+	if err != nil || isAlreadyMember {
+		return err
+	}
+
+	if err := organization.AddOrgUser(ctx, team.OrgID, user.ID); err != nil {
+		return err
+	}
+
+	err = db.WithTx(ctx, func(ctx context.Context) error {
+		// check in transaction
+		isAlreadyMember, err = organization.IsTeamMember(ctx, team.OrgID, team.ID, user.ID)
+		if err != nil || isAlreadyMember {
+			return err
+		}
+
+		sess := db.GetEngine(ctx)
+
+		if err := db.Insert(ctx, &organization.TeamUser{
+			UID:    user.ID,
+			OrgID:  team.OrgID,
+			TeamID: team.ID,
+		}); err != nil {
+			return err
+		} else if _, err := sess.Incr("num_members").ID(team.ID).Update(new(organization.Team)); err != nil {
+			return err
+		}
+
+		team.NumMembers++
+
+		// Give access to team repositories.
+		// update exist access if mode become bigger
+		subQuery := builder.Select("repo_id").From("team_repo").
+			Where(builder.Eq{"team_id": team.ID})
+
+		if _, err := sess.Where("user_id=?", user.ID).
+			In("repo_id", subQuery).
+			And("mode < ?", team.AccessMode).
+			SetExpr("mode", team.AccessMode).
+			Update(new(access_model.Access)); err != nil {
+			return fmt.Errorf("update user accesses: %w", err)
+		}
+
+		// for not exist access
+		var repoIDs []int64
+		accessSubQuery := builder.Select("repo_id").From("access").Where(builder.Eq{"user_id": user.ID})
+		if err := sess.SQL(subQuery.And(builder.NotIn("repo_id", accessSubQuery))).Find(&repoIDs); err != nil {
+			return fmt.Errorf("select id accesses: %w", err)
+		}
+
+		accesses := make([]*access_model.Access, 0, 100)
+		for i, repoID := range repoIDs {
+			accesses = append(accesses, &access_model.Access{RepoID: repoID, UserID: user.ID, Mode: team.AccessMode})
+			if (i%100 == 0 || i == len(repoIDs)-1) && len(accesses) > 0 {
+				if err = db.Insert(ctx, accesses); err != nil {
+					return fmt.Errorf("insert new user accesses: %w", err)
+				}
+				accesses = accesses[:0]
+			}
+		}
+		return nil
+	})
+	if err != nil {
+		return err
+	}
+
+	// this behaviour may spend much time so run it in a goroutine
+	// FIXME: Update watch repos batchly
+	if setting.Service.AutoWatchNewRepos {
+		// Get team and its repositories.
+		if err := team.LoadRepositories(ctx); err != nil {
+			log.Error("team.LoadRepositories failed: %v", err)
+		}
+
+		// FIXME: in the goroutine, it can't access the "ctx", it could only use db.DefaultContext at the moment
+		go func(repos []*repo_model.Repository) {
+			for _, repo := range repos {
+				if err = repo_model.WatchRepo(db.DefaultContext, user, repo, true); err != nil {
+					log.Error("watch repo failed: %v", err)
+				}
+			}
+		}(team.Repos)
+	}
+
+	return nil
+}
+
+// DeleteTeam deletes given team.
+// It's caller's responsibility to assign organization ID.
+func DeleteTeam(ctx context.Context, t *organization.Team) error {
+	ctx, committer, err := db.TxContext(ctx)
+	if err != nil {
+		return err
+	}
+	defer committer.Close()
+
+	if err := t.LoadRepositories(ctx); err != nil {
+		return err
+	}
+
+	if err := t.LoadMembers(ctx); err != nil {
+		return err
+	}
+
+	// update branch protections
+	{
+		protections := make([]*git_model.ProtectedBranch, 0, 10)
+		err := db.GetEngine(ctx).In("repo_id",
+			builder.Select("id").From("repository").Where(builder.Eq{"owner_id": t.OrgID})).
+			Find(&protections)
+		if err != nil {
+			return fmt.Errorf("findProtectedBranches: %w", err)
+		}
+		for _, p := range protections {
+			if err := git_model.RemoveTeamIDFromProtectedBranch(ctx, p, t.ID); err != nil {
+				return err
+			}
+		}
+	}
+
+	if err := repo_service.RemoveAllRepositoriesFromTeam(ctx, t); err != nil {
+		return err
+	}
+
+	if err := db.DeleteBeans(ctx,
+		&organization.Team{ID: t.ID},
+		&organization.TeamUser{OrgID: t.OrgID, TeamID: t.ID},
+		&organization.TeamUnit{TeamID: t.ID},
+		&organization.TeamInvite{TeamID: t.ID},
+		&issues_model.Review{Type: issues_model.ReviewTypeRequest, ReviewerTeamID: t.ID}, // batch delete the binding relationship between team and PR (request review from team)
+	); err != nil {
+		return err
+	}
+
+	for _, tm := range t.Members {
+		if err := removeInvalidOrgUser(ctx, t.OrgID, tm); err != nil {
+			return err
+		}
+	}
+
+	// Update organization number of teams.
+	if _, err := db.Exec(ctx, "UPDATE `user` SET num_teams=num_teams-1 WHERE id=?", t.OrgID); err != nil {
+		return err
+	}
+
+	return committer.Commit()
+}
+
+// RemoveTeamMember removes member from given team of given organization.
+func RemoveTeamMember(ctx context.Context, team *organization.Team, user *user_model.User) error {
+	ctx, committer, err := db.TxContext(ctx)
+	if err != nil {
+		return err
+	}
+	defer committer.Close()
+	if err := removeTeamMember(ctx, team, user); err != nil {
+		return err
+	}
+	return committer.Commit()
+}
+
+func removeTeamMember(ctx context.Context, team *organization.Team, user *user_model.User) error {
+	e := db.GetEngine(ctx)
+	isMember, err := organization.IsTeamMember(ctx, team.OrgID, team.ID, user.ID)
+	if err != nil || !isMember {
+		return err
+	}
+
+	// Check if the user to delete is the last member in owner team.
+	if team.IsOwnerTeam() && team.NumMembers == 1 {
+		return organization.ErrLastOrgOwner{UID: user.ID}
+	}
+
+	team.NumMembers--
+
+	if err := team.LoadRepositories(ctx); err != nil {
+		return err
+	}
+
+	if _, err := e.Delete(&organization.TeamUser{
+		UID:    user.ID,
+		OrgID:  team.OrgID,
+		TeamID: team.ID,
+	}); err != nil {
+		return err
+	} else if _, err = e.
+		ID(team.ID).
+		Cols("num_members").
+		Update(team); err != nil {
+		return err
+	}
+
+	// Delete access to team repositories.
+	for _, repo := range team.Repos {
+		if err := access_model.RecalculateUserAccess(ctx, repo, user.ID); err != nil {
+			return err
+		}
+
+		// Remove watches from now unaccessible
+		if err := models.ReconsiderWatches(ctx, repo, user); err != nil {
+			return err
+		}
+
+		// Remove issue assignments from now unaccessible
+		if err := models.ReconsiderRepoIssuesAssignee(ctx, repo, user); err != nil {
+			return err
+		}
+	}
+
+	return removeInvalidOrgUser(ctx, team.OrgID, user)
+}
+
+func removeInvalidOrgUser(ctx context.Context, orgID int64, user *user_model.User) error {
+	// Check if the user is a member of any team in the organization.
+	if count, err := db.GetEngine(ctx).Count(&organization.TeamUser{
+		UID:   user.ID,
+		OrgID: orgID,
+	}); err != nil {
+		return err
+	} else if count == 0 {
+		org, err := organization.GetOrgByID(ctx, orgID)
+		if err != nil {
+			return err
+		}
+
+		return RemoveOrgUser(ctx, org, user)
+	}
+	return nil
+}
diff --git a/models/org_team_test.go b/services/org/team_test.go
similarity index 61%
rename from models/org_team_test.go
rename to services/org/team_test.go
index cf2c8be536d8..d117013b1084 100644
--- a/models/org_team_test.go
+++ b/services/org/team_test.go
@@ -1,9 +1,10 @@
-// Copyright 2017 The Gitea Authors. All rights reserved.
+// Copyright 2024 The Gitea Authors. All rights reserved.
 // SPDX-License-Identifier: MIT
 
-package models
+package org
 
 import (
+	"fmt"
 	"strings"
 	"testing"
 
@@ -14,6 +15,8 @@ import (
 	repo_model "code.gitea.io/gitea/models/repo"
 	"code.gitea.io/gitea/models/unittest"
 	user_model "code.gitea.io/gitea/models/user"
+	"code.gitea.io/gitea/modules/structs"
+	repo_service "code.gitea.io/gitea/services/repository"
 
 	"github.com/stretchr/testify/assert"
 )
@@ -60,11 +63,6 @@ func TestTeam_RemoveMember(t *testing.T) {
 	assert.True(t, organization.IsErrLastOrgOwner(err))
 }
 
-func TestIsUsableTeamName(t *testing.T) {
-	assert.NoError(t, organization.IsUsableTeamName("usable"))
-	assert.True(t, db.IsErrNameReserved(organization.IsUsableTeamName("new")))
-}
-
 func TestNewTeam(t *testing.T) {
 	assert.NoError(t, unittest.PrepareTestDatabase())
 
@@ -185,3 +183,132 @@ func TestRepository_RecalculateAccesses3(t *testing.T) {
 	assert.NoError(t, err)
 	assert.True(t, has)
 }
+
+func TestIncludesAllRepositoriesTeams(t *testing.T) {
+	assert.NoError(t, unittest.PrepareTestDatabase())
+
+	testTeamRepositories := func(teamID int64, repoIDs []int64) {
+		team := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: teamID})
+		assert.NoError(t, team.LoadRepositories(db.DefaultContext), "%s: GetRepositories", team.Name)
+		assert.Len(t, team.Repos, team.NumRepos, "%s: len repo", team.Name)
+		assert.Len(t, team.Repos, len(repoIDs), "%s: repo count", team.Name)
+		for i, rid := range repoIDs {
+			if rid > 0 {
+				assert.True(t, repo_service.HasRepository(db.DefaultContext, team, rid), "%s: HasRepository(%d) %d", rid, i)
+			}
+		}
+	}
+
+	// Get an admin user.
+	user, err := user_model.GetUserByID(db.DefaultContext, 1)
+	assert.NoError(t, err, "GetUserByID")
+
+	// Create org.
+	org := &organization.Organization{
+		Name:       "All_repo",
+		IsActive:   true,
+		Type:       user_model.UserTypeOrganization,
+		Visibility: structs.VisibleTypePublic,
+	}
+	assert.NoError(t, organization.CreateOrganization(db.DefaultContext, org, user), "CreateOrganization")
+
+	// Check Owner team.
+	ownerTeam, err := org.GetOwnerTeam(db.DefaultContext)
+	assert.NoError(t, err, "GetOwnerTeam")
+	assert.True(t, ownerTeam.IncludesAllRepositories, "Owner team includes all repositories")
+
+	// Create repos.
+	repoIDs := make([]int64, 0)
+	for i := 0; i < 3; i++ {
+		r, err := repo_service.CreateRepositoryDirectly(db.DefaultContext, user, org.AsUser(), repo_service.CreateRepoOptions{Name: fmt.Sprintf("repo-%d", i)})
+		assert.NoError(t, err, "CreateRepository %d", i)
+		if r != nil {
+			repoIDs = append(repoIDs, r.ID)
+		}
+	}
+	// Get fresh copy of Owner team after creating repos.
+	ownerTeam, err = org.GetOwnerTeam(db.DefaultContext)
+	assert.NoError(t, err, "GetOwnerTeam")
+
+	// Create teams and check repositories.
+	teams := []*organization.Team{
+		ownerTeam,
+		{
+			OrgID:                   org.ID,
+			Name:                    "team one",
+			AccessMode:              perm.AccessModeRead,
+			IncludesAllRepositories: true,
+		},
+		{
+			OrgID:                   org.ID,
+			Name:                    "team 2",
+			AccessMode:              perm.AccessModeRead,
+			IncludesAllRepositories: false,
+		},
+		{
+			OrgID:                   org.ID,
+			Name:                    "team three",
+			AccessMode:              perm.AccessModeWrite,
+			IncludesAllRepositories: true,
+		},
+		{
+			OrgID:                   org.ID,
+			Name:                    "team 4",
+			AccessMode:              perm.AccessModeWrite,
+			IncludesAllRepositories: false,
+		},
+	}
+	teamRepos := [][]int64{
+		repoIDs,
+		repoIDs,
+		{},
+		repoIDs,
+		{},
+	}
+	for i, team := range teams {
+		if i > 0 { // first team is Owner.
+			assert.NoError(t, NewTeam(db.DefaultContext, team), "%s: NewTeam", team.Name)
+		}
+		testTeamRepositories(team.ID, teamRepos[i])
+	}
+
+	// Update teams and check repositories.
+	teams[3].IncludesAllRepositories = false
+	teams[4].IncludesAllRepositories = true
+	teamRepos[4] = repoIDs
+	for i, team := range teams {
+		assert.NoError(t, UpdateTeam(db.DefaultContext, team, false, true), "%s: UpdateTeam", team.Name)
+		testTeamRepositories(team.ID, teamRepos[i])
+	}
+
+	// Create repo and check teams repositories.
+	r, err := repo_service.CreateRepositoryDirectly(db.DefaultContext, user, org.AsUser(), repo_service.CreateRepoOptions{Name: "repo-last"})
+	assert.NoError(t, err, "CreateRepository last")
+	if r != nil {
+		repoIDs = append(repoIDs, r.ID)
+	}
+	teamRepos[0] = repoIDs
+	teamRepos[1] = repoIDs
+	teamRepos[4] = repoIDs
+	for i, team := range teams {
+		testTeamRepositories(team.ID, teamRepos[i])
+	}
+
+	// Remove repo and check teams repositories.
+	assert.NoError(t, repo_service.DeleteRepositoryDirectly(db.DefaultContext, user, repoIDs[0]), "DeleteRepository")
+	teamRepos[0] = repoIDs[1:]
+	teamRepos[1] = repoIDs[1:]
+	teamRepos[3] = repoIDs[1:3]
+	teamRepos[4] = repoIDs[1:]
+	for i, team := range teams {
+		testTeamRepositories(team.ID, teamRepos[i])
+	}
+
+	// Wipe created items.
+	for i, rid := range repoIDs {
+		if i > 0 { // first repo already deleted.
+			assert.NoError(t, repo_service.DeleteRepositoryDirectly(db.DefaultContext, user, rid), "DeleteRepository %d", i)
+		}
+	}
+	assert.NoError(t, organization.DeleteOrganization(db.DefaultContext, org), "DeleteOrganization")
+}
diff --git a/models/org.go b/services/org/user.go
similarity index 99%
rename from models/org.go
rename to services/org/user.go
index 69cc47137eb9..0627860fe7c0 100644
--- a/models/org.go
+++ b/services/org/user.go
@@ -2,7 +2,7 @@
 // Copyright 2019 The Gitea Authors. All rights reserved.
 // SPDX-License-Identifier: MIT
 
-package models
+package org
 
 import (
 	"context"
diff --git a/models/org_test.go b/services/org/user_test.go
similarity index 99%
rename from models/org_test.go
rename to services/org/user_test.go
index 247530406d6d..56d01a3b63a3 100644
--- a/models/org_test.go
+++ b/services/org/user_test.go
@@ -1,7 +1,7 @@
 // Copyright 2017 The Gitea Authors. All rights reserved.
 // SPDX-License-Identifier: MIT
 
-package models
+package org
 
 import (
 	"testing"
diff --git a/services/repository/create.go b/services/repository/create.go
index 0207f12a33f9..14e625d962a0 100644
--- a/services/repository/create.go
+++ b/services/repository/create.go
@@ -12,7 +12,6 @@ import (
 	"strings"
 	"time"
 
-	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/db"
 	"code.gitea.io/gitea/models/organization"
 	"code.gitea.io/gitea/models/perm"
@@ -448,7 +447,7 @@ func CreateRepositoryByExample(ctx context.Context, doer, u *user_model.User, re
 		}
 		for _, t := range teams {
 			if t.IncludesAllRepositories {
-				if err := models.AddRepository(ctx, t, repo); err != nil {
+				if err := addRepositoryToTeam(ctx, t, repo); err != nil {
 					return fmt.Errorf("AddRepository: %w", err)
 				}
 			}
diff --git a/services/repository/create_test.go b/services/repository/create_test.go
deleted file mode 100644
index 41e6b615db7f..000000000000
--- a/services/repository/create_test.go
+++ /dev/null
@@ -1,148 +0,0 @@
-// Copyright 2023 The Gitea Authors. All rights reserved.
-// SPDX-License-Identifier: MIT
-
-package repository
-
-import (
-	"fmt"
-	"testing"
-
-	"code.gitea.io/gitea/models"
-	"code.gitea.io/gitea/models/db"
-	"code.gitea.io/gitea/models/organization"
-	"code.gitea.io/gitea/models/perm"
-	"code.gitea.io/gitea/models/unittest"
-	user_model "code.gitea.io/gitea/models/user"
-	"code.gitea.io/gitea/modules/structs"
-
-	"github.com/stretchr/testify/assert"
-)
-
-func TestIncludesAllRepositoriesTeams(t *testing.T) {
-	assert.NoError(t, unittest.PrepareTestDatabase())
-
-	testTeamRepositories := func(teamID int64, repoIDs []int64) {
-		team := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: teamID})
-		assert.NoError(t, team.LoadRepositories(db.DefaultContext), "%s: GetRepositories", team.Name)
-		assert.Len(t, team.Repos, team.NumRepos, "%s: len repo", team.Name)
-		assert.Len(t, team.Repos, len(repoIDs), "%s: repo count", team.Name)
-		for i, rid := range repoIDs {
-			if rid > 0 {
-				assert.True(t, HasRepository(db.DefaultContext, team, rid), "%s: HasRepository(%d) %d", rid, i)
-			}
-		}
-	}
-
-	// Get an admin user.
-	user, err := user_model.GetUserByID(db.DefaultContext, 1)
-	assert.NoError(t, err, "GetUserByID")
-
-	// Create org.
-	org := &organization.Organization{
-		Name:       "All_repo",
-		IsActive:   true,
-		Type:       user_model.UserTypeOrganization,
-		Visibility: structs.VisibleTypePublic,
-	}
-	assert.NoError(t, organization.CreateOrganization(db.DefaultContext, org, user), "CreateOrganization")
-
-	// Check Owner team.
-	ownerTeam, err := org.GetOwnerTeam(db.DefaultContext)
-	assert.NoError(t, err, "GetOwnerTeam")
-	assert.True(t, ownerTeam.IncludesAllRepositories, "Owner team includes all repositories")
-
-	// Create repos.
-	repoIDs := make([]int64, 0)
-	for i := 0; i < 3; i++ {
-		r, err := CreateRepositoryDirectly(db.DefaultContext, user, org.AsUser(), CreateRepoOptions{Name: fmt.Sprintf("repo-%d", i)})
-		assert.NoError(t, err, "CreateRepository %d", i)
-		if r != nil {
-			repoIDs = append(repoIDs, r.ID)
-		}
-	}
-	// Get fresh copy of Owner team after creating repos.
-	ownerTeam, err = org.GetOwnerTeam(db.DefaultContext)
-	assert.NoError(t, err, "GetOwnerTeam")
-
-	// Create teams and check repositories.
-	teams := []*organization.Team{
-		ownerTeam,
-		{
-			OrgID:                   org.ID,
-			Name:                    "team one",
-			AccessMode:              perm.AccessModeRead,
-			IncludesAllRepositories: true,
-		},
-		{
-			OrgID:                   org.ID,
-			Name:                    "team 2",
-			AccessMode:              perm.AccessModeRead,
-			IncludesAllRepositories: false,
-		},
-		{
-			OrgID:                   org.ID,
-			Name:                    "team three",
-			AccessMode:              perm.AccessModeWrite,
-			IncludesAllRepositories: true,
-		},
-		{
-			OrgID:                   org.ID,
-			Name:                    "team 4",
-			AccessMode:              perm.AccessModeWrite,
-			IncludesAllRepositories: false,
-		},
-	}
-	teamRepos := [][]int64{
-		repoIDs,
-		repoIDs,
-		{},
-		repoIDs,
-		{},
-	}
-	for i, team := range teams {
-		if i > 0 { // first team is Owner.
-			assert.NoError(t, models.NewTeam(db.DefaultContext, team), "%s: NewTeam", team.Name)
-		}
-		testTeamRepositories(team.ID, teamRepos[i])
-	}
-
-	// Update teams and check repositories.
-	teams[3].IncludesAllRepositories = false
-	teams[4].IncludesAllRepositories = true
-	teamRepos[4] = repoIDs
-	for i, team := range teams {
-		assert.NoError(t, models.UpdateTeam(db.DefaultContext, team, false, true), "%s: UpdateTeam", team.Name)
-		testTeamRepositories(team.ID, teamRepos[i])
-	}
-
-	// Create repo and check teams repositories.
-	r, err := CreateRepositoryDirectly(db.DefaultContext, user, org.AsUser(), CreateRepoOptions{Name: "repo-last"})
-	assert.NoError(t, err, "CreateRepository last")
-	if r != nil {
-		repoIDs = append(repoIDs, r.ID)
-	}
-	teamRepos[0] = repoIDs
-	teamRepos[1] = repoIDs
-	teamRepos[4] = repoIDs
-	for i, team := range teams {
-		testTeamRepositories(team.ID, teamRepos[i])
-	}
-
-	// Remove repo and check teams repositories.
-	assert.NoError(t, DeleteRepositoryDirectly(db.DefaultContext, user, repoIDs[0]), "DeleteRepository")
-	teamRepos[0] = repoIDs[1:]
-	teamRepos[1] = repoIDs[1:]
-	teamRepos[3] = repoIDs[1:3]
-	teamRepos[4] = repoIDs[1:]
-	for i, team := range teams {
-		testTeamRepositories(team.ID, teamRepos[i])
-	}
-
-	// Wipe created items.
-	for i, rid := range repoIDs {
-		if i > 0 { // first repo already deleted.
-			assert.NoError(t, DeleteRepositoryDirectly(db.DefaultContext, user, rid), "DeleteRepository %d", i)
-		}
-	}
-	assert.NoError(t, organization.DeleteOrganization(db.DefaultContext, org), "DeleteOrganization")
-}
diff --git a/services/repository/delete.go b/services/repository/delete.go
index e58083314089..f33bae779011 100644
--- a/services/repository/delete.go
+++ b/services/repository/delete.go
@@ -348,87 +348,6 @@ func DeleteRepositoryDirectly(ctx context.Context, doer *user_model.User, repoID
 	return nil
 }
 
-// removeRepositoryFromTeam removes a repository from a team and recalculates access
-// Note: Repository shall not be removed from team if it includes all repositories (unless the repository is deleted)
-func removeRepositoryFromTeam(ctx context.Context, t *organization.Team, repo *repo_model.Repository, recalculate bool) (err error) {
-	e := db.GetEngine(ctx)
-	if err = organization.RemoveTeamRepo(ctx, t.ID, repo.ID); err != nil {
-		return err
-	}
-
-	t.NumRepos--
-	if _, err = e.ID(t.ID).Cols("num_repos").Update(t); err != nil {
-		return err
-	}
-
-	// Don't need to recalculate when delete a repository from organization.
-	if recalculate {
-		if err = access_model.RecalculateTeamAccesses(ctx, repo, t.ID); err != nil {
-			return err
-		}
-	}
-
-	teamMembers, err := organization.GetTeamMembers(ctx, &organization.SearchMembersOptions{
-		TeamID: t.ID,
-	})
-	if err != nil {
-		return fmt.Errorf("GetTeamMembers: %w", err)
-	}
-	for _, member := range teamMembers {
-		has, err := access_model.HasAnyUnitAccess(ctx, member.ID, repo)
-		if err != nil {
-			return err
-		} else if has {
-			continue
-		}
-
-		if err = repo_model.WatchRepo(ctx, member, repo, false); err != nil {
-			return err
-		}
-
-		// Remove all IssueWatches a user has subscribed to in the repositories
-		if err := issues_model.RemoveIssueWatchersByRepoID(ctx, member.ID, repo.ID); err != nil {
-			return err
-		}
-	}
-
-	return nil
-}
-
-// HasRepository returns true if given repository belong to team.
-func HasRepository(ctx context.Context, t *organization.Team, repoID int64) bool {
-	return organization.HasTeamRepo(ctx, t.OrgID, t.ID, repoID)
-}
-
-// RemoveRepositoryFromTeam removes repository from team of organization.
-// If the team shall include all repositories the request is ignored.
-func RemoveRepositoryFromTeam(ctx context.Context, t *organization.Team, repoID int64) error {
-	if !HasRepository(ctx, t, repoID) {
-		return nil
-	}
-
-	if t.IncludesAllRepositories {
-		return nil
-	}
-
-	repo, err := repo_model.GetRepositoryByID(ctx, repoID)
-	if err != nil {
-		return err
-	}
-
-	ctx, committer, err := db.TxContext(ctx)
-	if err != nil {
-		return err
-	}
-	defer committer.Close()
-
-	if err = removeRepositoryFromTeam(ctx, t, repo, true); err != nil {
-		return err
-	}
-
-	return committer.Commit()
-}
-
 // DeleteOwnerRepositoriesDirectly calls DeleteRepositoryDirectly for all repos of the given owner
 func DeleteOwnerRepositoriesDirectly(ctx context.Context, owner *user_model.User) error {
 	for {
diff --git a/services/repository/repo_team.go b/services/repository/repo_team.go
new file mode 100644
index 000000000000..29c67893b23c
--- /dev/null
+++ b/services/repository/repo_team.go
@@ -0,0 +1,226 @@
+// Copyright 2024 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package repository
+
+import (
+	"context"
+	"errors"
+	"fmt"
+
+	"code.gitea.io/gitea/models/db"
+	issues_model "code.gitea.io/gitea/models/issues"
+	"code.gitea.io/gitea/models/organization"
+	access_model "code.gitea.io/gitea/models/perm/access"
+	repo_model "code.gitea.io/gitea/models/repo"
+	"code.gitea.io/gitea/modules/setting"
+)
+
+// TeamAddRepository adds new repository to team of organization.
+func TeamAddRepository(ctx context.Context, t *organization.Team, repo *repo_model.Repository) (err error) {
+	if repo.OwnerID != t.OrgID {
+		return errors.New("repository does not belong to organization")
+	} else if organization.HasTeamRepo(ctx, t.OrgID, t.ID, repo.ID) {
+		return nil
+	}
+
+	return db.WithTx(ctx, func(ctx context.Context) error {
+		return addRepositoryToTeam(ctx, t, repo)
+	})
+}
+
+func addRepositoryToTeam(ctx context.Context, t *organization.Team, repo *repo_model.Repository) (err error) {
+	if err = organization.AddTeamRepo(ctx, t.OrgID, t.ID, repo.ID); err != nil {
+		return err
+	}
+
+	if err = organization.IncrTeamRepoNum(ctx, t.ID); err != nil {
+		return fmt.Errorf("update team: %w", err)
+	}
+
+	t.NumRepos++
+
+	if err = access_model.RecalculateTeamAccesses(ctx, repo, 0); err != nil {
+		return fmt.Errorf("recalculateAccesses: %w", err)
+	}
+
+	// Make all team members watch this repo if enabled in global settings
+	if setting.Service.AutoWatchNewRepos {
+		if err = t.LoadMembers(ctx); err != nil {
+			return fmt.Errorf("getMembers: %w", err)
+		}
+		for _, u := range t.Members {
+			if err = repo_model.WatchRepo(ctx, u, repo, true); err != nil {
+				return fmt.Errorf("watchRepo: %w", err)
+			}
+		}
+	}
+
+	return nil
+}
+
+// AddAllRepositoriesToTeam adds all repositories to the team.
+// If the team already has some repositories they will be left unchanged.
+func AddAllRepositoriesToTeam(ctx context.Context, t *organization.Team) error {
+	return db.WithTx(ctx, func(ctx context.Context) error {
+		orgRepos, err := organization.GetOrgRepositories(ctx, t.OrgID)
+		if err != nil {
+			return fmt.Errorf("get org repos: %w", err)
+		}
+
+		for _, repo := range orgRepos {
+			if !organization.HasTeamRepo(ctx, t.OrgID, t.ID, repo.ID) {
+				if err := addRepositoryToTeam(ctx, t, repo); err != nil {
+					return fmt.Errorf("AddRepository: %w", err)
+				}
+			}
+		}
+
+		return nil
+	})
+}
+
+// RemoveAllRepositoriesFromTeam removes all repositories from team and recalculates access
+func RemoveAllRepositoriesFromTeam(ctx context.Context, t *organization.Team) (err error) {
+	if t.IncludesAllRepositories {
+		return nil
+	}
+
+	ctx, committer, err := db.TxContext(ctx)
+	if err != nil {
+		return err
+	}
+	defer committer.Close()
+
+	if err = removeAllRepositoriesFromTeam(ctx, t); err != nil {
+		return err
+	}
+
+	return committer.Commit()
+}
+
+// removeAllRepositoriesFromTeam removes all repositories from team and recalculates access
+// Note: Shall not be called if team includes all repositories
+func removeAllRepositoriesFromTeam(ctx context.Context, t *organization.Team) (err error) {
+	e := db.GetEngine(ctx)
+	// Delete all accesses.
+	for _, repo := range t.Repos {
+		if err := access_model.RecalculateTeamAccesses(ctx, repo, t.ID); err != nil {
+			return err
+		}
+
+		// Remove watches from all users and now unaccessible repos
+		for _, user := range t.Members {
+			has, err := access_model.HasAnyUnitAccess(ctx, user.ID, repo)
+			if err != nil {
+				return err
+			} else if has {
+				continue
+			}
+
+			if err = repo_model.WatchRepo(ctx, user, repo, false); err != nil {
+				return err
+			}
+
+			// Remove all IssueWatches a user has subscribed to in the repositories
+			if err = issues_model.RemoveIssueWatchersByRepoID(ctx, user.ID, repo.ID); err != nil {
+				return err
+			}
+		}
+	}
+
+	// Delete team-repo
+	if _, err := e.
+		Where("team_id=?", t.ID).
+		Delete(new(organization.TeamRepo)); err != nil {
+		return err
+	}
+
+	t.NumRepos = 0
+	if _, err = e.ID(t.ID).Cols("num_repos").Update(t); err != nil {
+		return err
+	}
+
+	return nil
+}
+
+// RemoveRepositoryFromTeam removes repository from team of organization.
+// If the team shall include all repositories the request is ignored.
+func RemoveRepositoryFromTeam(ctx context.Context, t *organization.Team, repoID int64) error {
+	if !HasRepository(ctx, t, repoID) {
+		return nil
+	}
+
+	if t.IncludesAllRepositories {
+		return nil
+	}
+
+	repo, err := repo_model.GetRepositoryByID(ctx, repoID)
+	if err != nil {
+		return err
+	}
+
+	ctx, committer, err := db.TxContext(ctx)
+	if err != nil {
+		return err
+	}
+	defer committer.Close()
+
+	if err = removeRepositoryFromTeam(ctx, t, repo, true); err != nil {
+		return err
+	}
+
+	return committer.Commit()
+}
+
+// removeRepositoryFromTeam removes a repository from a team and recalculates access
+// Note: Repository shall not be removed from team if it includes all repositories (unless the repository is deleted)
+func removeRepositoryFromTeam(ctx context.Context, t *organization.Team, repo *repo_model.Repository, recalculate bool) (err error) {
+	e := db.GetEngine(ctx)
+	if err = organization.RemoveTeamRepo(ctx, t.ID, repo.ID); err != nil {
+		return err
+	}
+
+	t.NumRepos--
+	if _, err = e.ID(t.ID).Cols("num_repos").Update(t); err != nil {
+		return err
+	}
+
+	// Don't need to recalculate when delete a repository from organization.
+	if recalculate {
+		if err = access_model.RecalculateTeamAccesses(ctx, repo, t.ID); err != nil {
+			return err
+		}
+	}
+
+	teamMembers, err := organization.GetTeamMembers(ctx, &organization.SearchMembersOptions{
+		TeamID: t.ID,
+	})
+	if err != nil {
+		return fmt.Errorf("GetTeamMembers: %w", err)
+	}
+	for _, member := range teamMembers {
+		has, err := access_model.HasAnyUnitAccess(ctx, member.ID, repo)
+		if err != nil {
+			return err
+		} else if has {
+			continue
+		}
+
+		if err = repo_model.WatchRepo(ctx, member, repo, false); err != nil {
+			return err
+		}
+
+		// Remove all IssueWatches a user has subscribed to in the repositories
+		if err := issues_model.RemoveIssueWatchersByRepoID(ctx, member.ID, repo.ID); err != nil {
+			return err
+		}
+	}
+
+	return nil
+}
+
+// HasRepository returns true if given repository belong to team.
+func HasRepository(ctx context.Context, t *organization.Team, repoID int64) bool {
+	return organization.HasTeamRepo(ctx, t.OrgID, t.ID, repoID)
+}
diff --git a/services/org/repo_test.go b/services/repository/repo_team_test.go
similarity index 98%
rename from services/org/repo_test.go
rename to services/repository/repo_team_test.go
index 68c64a01ab4a..70b1b47d0af8 100644
--- a/services/org/repo_test.go
+++ b/services/repository/repo_team_test.go
@@ -1,7 +1,7 @@
 // Copyright 2021 The Gitea Authors. All rights reserved.
 // SPDX-License-Identifier: MIT
 
-package org
+package repository
 
 import (
 	"testing"
diff --git a/services/repository/transfer.go b/services/repository/transfer.go
index 301d89533781..9a643469d9db 100644
--- a/services/repository/transfer.go
+++ b/services/repository/transfer.go
@@ -59,7 +59,7 @@ func TransferOwnership(ctx context.Context, doer, newOwner *user_model.User, rep
 	}
 
 	for _, team := range teams {
-		if err := models.AddRepository(ctx, team, newRepo); err != nil {
+		if err := addRepositoryToTeam(ctx, team, newRepo); err != nil {
 			return err
 		}
 	}
@@ -205,7 +205,7 @@ func transferOwnership(ctx context.Context, doer *user_model.User, newOwnerName
 		}
 		for _, t := range teams {
 			if t.IncludesAllRepositories {
-				if err := models.AddRepository(ctx, t, repo); err != nil {
+				if err := addRepositoryToTeam(ctx, t, repo); err != nil {
 					return fmt.Errorf("AddRepository: %w", err)
 				}
 			}
diff --git a/services/user/user.go b/services/user/user.go
index 7855dbb78b71..7bde642412be 100644
--- a/services/user/user.go
+++ b/services/user/user.go
@@ -188,7 +188,7 @@ func DeleteUser(ctx context.Context, u *user_model.User, purge bool) error {
 				break
 			}
 			for _, org := range orgs {
-				if err := models.RemoveOrgUser(ctx, org, u); err != nil {
+				if err := org_service.RemoveOrgUser(ctx, org, u); err != nil {
 					if organization.IsErrLastOrgOwner(err) {
 						err = org_service.DeleteOrganization(ctx, org, true)
 						if err != nil {
diff --git a/services/user/user_test.go b/services/user/user_test.go
index efcbc669c8a3..c668b005c57e 100644
--- a/services/user/user_test.go
+++ b/services/user/user_test.go
@@ -18,6 +18,7 @@ import (
 	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/setting"
 	"code.gitea.io/gitea/modules/timeutil"
+	org_service "code.gitea.io/gitea/services/org"
 
 	"github.com/stretchr/testify/assert"
 )
@@ -44,7 +45,7 @@ func TestDeleteUser(t *testing.T) {
 		assert.NoError(t, db.GetEngine(db.DefaultContext).Find(&orgUsers, &organization.OrgUser{UID: userID}))
 		for _, orgUser := range orgUsers {
 			org := unittest.AssertExistsAndLoadBean(t, &organization.Organization{ID: orgUser.OrgID})
-			if err := models.RemoveOrgUser(db.DefaultContext, org, user); err != nil {
+			if err := org_service.RemoveOrgUser(db.DefaultContext, org, user); err != nil {
 				assert.True(t, organization.IsErrLastOrgOwner(err))
 				return
 			}
diff --git a/tests/integration/auth_ldap_test.go b/tests/integration/auth_ldap_test.go
index 8c8b6b02d145..00ef72c1c3f9 100644
--- a/tests/integration/auth_ldap_test.go
+++ b/tests/integration/auth_ldap_test.go
@@ -10,7 +10,6 @@ import (
 	"strings"
 	"testing"
 
-	"code.gitea.io/gitea/models"
 	auth_model "code.gitea.io/gitea/models/auth"
 	"code.gitea.io/gitea/models/db"
 	"code.gitea.io/gitea/models/organization"
@@ -19,6 +18,7 @@ import (
 	"code.gitea.io/gitea/modules/translation"
 	"code.gitea.io/gitea/services/auth"
 	"code.gitea.io/gitea/services/auth/source/ldap"
+	org_service "code.gitea.io/gitea/services/org"
 	"code.gitea.io/gitea/tests"
 
 	"github.com/stretchr/testify/assert"
@@ -429,9 +429,9 @@ func TestLDAPGroupTeamSyncAddMember(t *testing.T) {
 			isMember, err := organization.IsTeamMember(db.DefaultContext, usersOrgs[0].ID, team.ID, user.ID)
 			assert.NoError(t, err)
 			assert.True(t, isMember, "Membership should be added to the right team")
-			err = models.RemoveTeamMember(db.DefaultContext, team, user)
+			err = org_service.RemoveTeamMember(db.DefaultContext, team, user)
 			assert.NoError(t, err)
-			err = models.RemoveOrgUser(db.DefaultContext, usersOrgs[0], user)
+			err = org_service.RemoveOrgUser(db.DefaultContext, usersOrgs[0], user)
 			assert.NoError(t, err)
 		} else {
 			// assert members of LDAP group "cn=admin_staff" keep initial team membership since mapped team does not exist
@@ -461,7 +461,7 @@ func TestLDAPGroupTeamSyncRemoveMember(t *testing.T) {
 	})
 	err = organization.AddOrgUser(db.DefaultContext, org.ID, user.ID)
 	assert.NoError(t, err)
-	err = models.AddTeamMember(db.DefaultContext, team, user)
+	err = org_service.AddTeamMember(db.DefaultContext, team, user)
 	assert.NoError(t, err)
 	isMember, err := organization.IsOrganizationMember(db.DefaultContext, org.ID, user.ID)
 	assert.NoError(t, err)