@@ -738,6 +738,33 @@ async def get_users_count_by_admin(db: AsyncSession, admin_id: int | None) -> in
738738 return (await db .execute (stmt )).scalar_one () or 0
739739
740740
741+ async def lock_admin_quota_row (db : AsyncSession , admin_id : int ) -> None :
742+ """Lock an admin row before quota-sensitive user creation."""
743+ if db .bind .dialect .name == "sqlite" :
744+ await db .execute (update (Admin ).where (Admin .id == admin_id ).values (id = Admin .id ))
745+ return
746+
747+ await db .execute (select (Admin .id ).where (Admin .id == admin_id ).with_for_update ())
748+
749+
750+ async def get_users_by_usernames (db : AsyncSession , usernames : Sequence [str ]) -> list [User ]:
751+ if not usernames :
752+ return []
753+
754+ result = await db .execute (_build_user_select_stmt ().where (User .username .in_ (usernames )))
755+ users_by_username = {user .username : user for user in result .unique ().scalars ().all ()}
756+ return [users_by_username [username ] for username in usernames if username in users_by_username ]
757+
758+
759+ async def get_users_by_ids (db : AsyncSession , user_ids : Sequence [int ]) -> list [User ]:
760+ if not user_ids :
761+ return []
762+
763+ result = await db .execute (_build_user_select_stmt ().where (User .id .in_ (user_ids )))
764+ users_by_id = {user .id : user for user in result .unique ().scalars ().all ()}
765+ return [users_by_id [user_id ] for user_id in user_ids if user_id in users_by_id ]
766+
767+
741768async def get_users_count (db : AsyncSession , status : UserStatus = None , admin_id : int = None ) -> int :
742769 """
743770 Gets the total count of users with optional filters.
@@ -797,7 +824,9 @@ async def get_users_count_by_status(
797824 return all_statuses
798825
799826
800- async def create_user (db : AsyncSession , new_user : UserCreate , groups : list [Group ], admin : Admin ) -> User :
827+ async def create_user (
828+ db : AsyncSession , new_user : UserCreate , groups : list [Group ], admin : Admin , * , commit : bool = True
829+ ) -> User :
801830 """
802831 Creates a new user.
803832
@@ -829,13 +858,14 @@ async def create_user(db: AsyncSession, new_user: UserCreate, groups: list[Group
829858 if new_user .next_plan :
830859 db_user .next_plan = NextPlan (user_id = db_user .id , ** new_user .next_plan .model_dump ())
831860 db .add (db_user .next_plan )
832- await db .commit ()
833- await refresh_and_load_user (db , db_user )
861+ if commit :
862+ await db .commit ()
863+ await refresh_and_load_user (db , db_user )
834864 return db_user
835865
836866
837867async def create_users_bulk (
838- db : AsyncSession , new_users : list [UserCreate ], groups : list [Group ], admin : Admin
868+ db : AsyncSession , new_users : list [UserCreate ], groups : list [Group ], admin : Admin , * , commit : bool = True
839869) -> list [User ]:
840870 """
841871 Creates multiple users in a single commit for better performance.
@@ -868,10 +898,10 @@ async def create_users_bulk(
868898 db .add_all (next_plans )
869899 await db .flush ()
870900
871- await db . commit ()
872-
873- for user in db_users :
874- await refresh_and_load_user (db , user )
901+ if commit :
902+ await db . commit ()
903+ for user in db_users :
904+ await refresh_and_load_user (db , user )
875905
876906 return db_users
877907
@@ -925,6 +955,7 @@ async def modify_user(
925955 modify : UserModify ,
926956 * ,
927957 groups : list [Group ] | None = None ,
958+ commit : bool = True ,
928959) -> User :
929960 """
930961 Modify a user's information.
@@ -1019,8 +1050,9 @@ async def modify_user(
10191050 if remove_expiration_reminder :
10201051 await delete_user_passed_notification_reminders (db , id , ReminderType .expiration_date , days_left )
10211052
1022- await db .commit ()
1023- await refresh_and_load_user (db , db_user )
1053+ if commit :
1054+ await db .commit ()
1055+ await refresh_and_load_user (db , db_user )
10241056 return db_user
10251057
10261058
@@ -1047,7 +1079,9 @@ async def clear_user_node_usages(db: AsyncSession, user_id: int, *, before: date
10471079 await db .execute (stmt )
10481080
10491081
1050- async def reset_user_data_usage (db : AsyncSession , db_user : User , * , clean_chart_data : bool = False ) -> User :
1082+ async def reset_user_data_usage (
1083+ db : AsyncSession , db_user : User , * , clean_chart_data : bool = False , commit : bool = True
1084+ ) -> User :
10511085 """
10521086 Resets the data usage of a user and logs the reset.
10531087
@@ -1065,13 +1099,14 @@ async def reset_user_data_usage(db: AsyncSession, db_user: User, *, clean_chart_
10651099 if db_user .status not in [UserStatus .expired , UserStatus .disabled ]:
10661100 db_user .status = UserStatus .active
10671101
1068- await db .commit ()
1069- await refresh_and_load_user (db , db_user )
1102+ if commit :
1103+ await db .commit ()
1104+ await refresh_and_load_user (db , db_user )
10701105 return db_user
10711106
10721107
10731108async def bulk_reset_user_data_usage (
1074- db : AsyncSession , users : list [User ], * , clean_chart_data : bool = False
1109+ db : AsyncSession , users : list [User ], * , clean_chart_data : bool = False , commit : bool = True
10751110) -> list [User ]:
10761111 """
10771112 Resets the data usage for a list of users and logs the reset.
@@ -1089,9 +1124,10 @@ async def bulk_reset_user_data_usage(
10891124 await clear_user_node_usages (db , db_user .id )
10901125 if db_user .status not in [UserStatus .expired , UserStatus .disabled ]:
10911126 db_user .status = UserStatus .active
1092- await db .commit ()
1093- for user in users :
1094- await refresh_and_load_user (db , user )
1127+ if commit :
1128+ await db .commit ()
1129+ for user in users :
1130+ await refresh_and_load_user (db , user )
10951131 return users
10961132
10971133
0 commit comments